Springboot 中接口服务重试机制

在平时开发中可能在调用服务时会遇到调用失败的情况,在springboot 中retery 机制可以很好的满足我们的开发场景,下面举个简单的例子模拟第三方调用。

package com.szhome.web.action;import com.szhome.web.service.ThirdApiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap;
import java.util.Map;/*** @Author caizl* @Description TODO* @Date 2024/01/10/8:43* @Version 1.0*/
@Controller
@RequestMapping("/api")
public class ThirdApiAction {@Autowiredprivate ThirdApiService thirdApiService;@RequestMapping("/third")@ResponseBodypublic Map<String,String> callThirdApi(@RequestParam String id)throws Exception{System.out.println("开始调用第三方接口");thirdApiService.callThirdApi(id);Map<String,String> map = new HashMap<>();map.put("code","ok");map.put("msg","执行成功");return map;}}package com.szhome.web.service;import com.google.gson.Gson;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;import java.util.HashMap;
import java.util.Map;/*** @Author caizl* @Description TODO* @Date 2024/01/10/8:44* @Version 1.0*/
@Service
public class ThirdApiService {/*** value:抛出指定异常才会重试* include:和value一样,默认为空,当exclude也为空时,默认所有异常* exclude:指定不处理的异常* maxAttempts:最大重试次数,默认3次* backoff:重试等待策略,* 默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000; 以毫秒为单位的延迟(默认 1000)* multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。* @return*/@Retryable(value = { RestClientException.class },//异常时进行重试maxAttempts = 3,//最大重试次数backoff = @Backoff(delay = 1000, multiplier = 2)//backoff指定了重试间隔的初始延迟和延迟倍数。)public String callThirdApi(String id)throws RestClientException{System.out.println("第三方服务类。。。。start。。。。");Map<String,String> map = new HashMap<>();Gson gson = new Gson();try{map.put("code","1");throw  new RestClientException("测试带三方重试接口"+id);}catch (Exception e){map.put("code","0");throw e;}/*finally {return gson.toJson(map);}*/}@Recoverpublic String lastProcess(String id){System.out.println("第三方服务失败后最终执行业务,模拟记录日志,id=" + id);return "ok";}
}

使用起来很简单,只需要在引入相关jar,并且在启动的时候进行开启,这是springboot 的老套路,在我们服务层进行 @Retryable 的配置,在重试机制完成后我们可以配置一个兜底服务@Recover,我们可以接收请求参数,以此我们后续还可以进行补偿服务的延伸扩展使我们的服务更加的灵活健硕。

以下重试机制的参数说明及相关类使用:

@Backoff
重试回退策略(立即重试还是等待一会再重试)
value:重试延迟时间,单位毫秒,默认值1000,即默认延迟1秒。当未设置multiplier时,表示每隔value的时间重试,直到重试次数到达maxAttempts设置的最大允许重试次数。当设置了multiplier参数时,该值作为幂运算的初始值。
delay:等同value参数,两个参数设置一个即可。
maxDelay:两次重试间最大间隔时间。当设置multiplier参数后,下次延迟时间根据是上次延迟时间乘以 multiplier得出的,这会导致两次重试间的延迟时间越来越长,该参数限制两次重试的最大间隔时间,当间隔时间大于该值时,计算出的间隔时间将会被忽略,使用上次的重试间隔时间。
multiplier:作为乘数用于计算下次延迟时间。公式:delay = delay * multiplier
random:是否启用随机退避策略,默认false。设置为true时启用退避策略,重试延迟时间将是delay和maxDelay间的一个随机数。设置该参数的目的是重试的时候避免同时发起重试请求,造成Ddos攻击。
@CircuitBreaker
include: 指定处理的异常类。默认为空
exclude: 指定不需要处理的异常。默认为空
vaue: 指定要重试的异常。默认为空
maxAttempts: 最大重试次数。默认3次
openTimeout: 配置熔断器打开的超时时间,默认5s,当超过openTimeout之后熔断器电路变成半打开状态(只要有一次重试成功,则闭合电路)
resetTimeout: 配置熔断器重新闭合的超时时间,默认20s,超过这个时间断路器关闭

@Service
class BusinessService {@Recoverpublic int fallback(BoomException ex) {return 2;}@CircuitBreaker(include = Exception.class, openTimeout = 20000L, resetTimeout = 5000L, maxAttempts = 1)public int desireNumber() throws Exception {System.out.println("calling desireNumber()");if (Math.random() > .5) {throw new Exception("error");}return 1;}
}

RetryTemplate
什么时候使用RetryTemplate?

不使用spring容器的时候,使用了@Retryable,@CircuitBreaker的方法不能在本类被调用,不然重试机制不会生效。也就是要标记为@Service,然后在其它类使用@Autowired注入或者@Bean去实例才能生效。
需要使用复杂策略机制和异常场景时
使用有状态重试,且需要全局模式时建议使用
需要使用监听器Listener的场景
需要使用Retry统计分析
RetryPolicy 重试策略
NeverRetryPolicy:只允许调用RetryCallback一次,不允许重试;
AlwaysRetryPolicy:允许无限重试,直到成功,此方式逻辑不当会导致死循环;
SimpleRetryPolicy:固定次数重试策略,默认重试最大次数为3次,RetryTemplate默认使用的策略;
TimeoutRetryPolicy:超时时间重试策略,默认超时时间为1秒,在指定的超时时间内允许重试;
CircuitBreakerRetryPolicy:有熔断功能的重试策略,需设置3个参数openTimeout、resetTimeout和delegate,稍后详细介绍该策略;
CompositeRetryPolicy:组合重试策略,有两种组合方式,乐观组合重试策略是指只要有一个策略允许重试即可以,悲观组合重试策略是指只要有一个策略不允许重试即可以,但不管哪种组合方式,组合中的每一个策略都会执行。
BackOffPolicy 退避策略
NoBackOffPolicy:无退避算法策略,即当重试时是立即重试;
FixedBackOffPolicy:固定时间的退避策略,需设置参数sleeper和backOffPeriod,sleeper指定等待策略,默认是Thread.sleep,即线程休眠,backOffPeriod指定休眠时间,默认1秒;
UniformRandomBackOffPolicy:随机时间退避策略,需设置sleeper、minBackOffPeriod和maxBackOffPeriod,该策略在[minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间,minBackOffPeriod默认500毫秒,maxBackOffPeriod默认1500毫秒;
ExponentialBackOffPolicy:指数退避策略,需设置参数sleeper、initialInterval、maxInterval和multiplier,initialInterval指定初始休眠时间,默认100毫秒,maxInterval指定最大休眠时间,默认30秒,multiplier指定乘数,即下一次休眠时间为当前休眠时间*multiplier;
ExponentialRandomBackOffPolicy:随机指数退避策略,引入随机乘数,之前说过固定乘数可能会引起很多服务同时重试导致DDos,使用随机休眠时间来避免这种情况。
DEMO RetryTemplate
TimeoutRetryPolicy
TimeoutRetryPolicy策略,TimeoutRetryPolicy超时时间默认是1秒。
TimeoutRetryPolicy超时是指在execute方法内部,从open操作开始到调用TimeoutRetryPolicy的canRetry方法这之间所经过的时间。
这段时间未超过TimeoutRetryPolicy定义的超时时间,那么执行操作,否则抛出异常。
当重试执行完闭,操作还未成为,那么可以通过RecoveryCallback完成一些失败事后处理。
public class RetryTemplate01 {
    public static void main(String[] args) throws Exception {
        RetryTemplate template = new RetryTemplate();
        TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
        template.setRetryPolicy(policy);
        String result = template.execute(new RetryCallback<String, Exception>() {
            public String doWithRetry(RetryContext arg0) throws Exception {
                return "Retry";
            }
        });
        System.out.println(result);
    }
}
SimpleRetryPolicy
代码重试两次后,仍然失败,RecoveryCallback被调用,返回”recovery callback”。如果没有定义RecoveryCallback,那么重试2次后,将会抛出异常。

public class RetryTemplate02 {

    public static void main(String[] args) throws Exception {
        RetryTemplate template = new RetryTemplate();
        SimpleRetryPolicy policy = new SimpleRetryPolicy();
        policy.setMaxAttempts(2);
        template.setRetryPolicy(policy);
        String result = template.execute(new RetryCallback<String, Exception>() {
            public String doWithRetry(RetryContext arg0) throws Exception {
                throw new NullPointerException("nullPointerException");
            }
        }, new RecoveryCallback<String>() {
            public String recover(RetryContext context) throws Exception {
                return "recovery callback";
            }
        });
        System.out.println(result);
    }
}
该策略定义了对指定的异常进行若干次重试。默认情况下,对Exception异常及其子类重试3次。 如果创建SimpleRetryPolicy并指定重试异常map,可以选择性重试或不进行重试。下面的代码定义了对TimeOutException进行重试。

public class RetryTemplate03 {

    public static void main(String[] args) throws Exception {
        
        RetryTemplate template = new RetryTemplate();
        Map<Class<? extends Throwable>, Boolean> maps = new HashMap<Class<? extends Throwable>, Boolean>();
        maps.put(TimeoutException.class, true);
        SimpleRetryPolicy policy2 = new SimpleRetryPolicy(2, maps);
        template.setRetryPolicy(policy2);

        String result = template.execute(new RetryCallback<String, Exception>() {
            public String doWithRetry(RetryContext arg0) throws Exception {
                throw new TimeoutException("TimeoutException");
            }
        }, new RecoveryCallback<String>() {
            public String recover(RetryContext context) throws Exception {
                return "recovery callback";
            }
        });
        System.out.println(result);
    }

}
ExceptionClassifierRetryPolicy
通过PolicyMap定义异常及其重试策略。下面的代码在抛出NullPointerException采用NeverRetryPolicy策略,而TimeoutException采用AlwaysRetryPolicy。

public class RetryTemplate04 {

    public static void main(String[] args) throws Exception {
        RetryTemplate template = new RetryTemplate();
        ExceptionClassifierRetryPolicy policy = new ExceptionClassifierRetryPolicy();
        Map<Class<? extends Throwable>, RetryPolicy> policyMap = new HashMap<Class<? extends Throwable>, RetryPolicy>();
        policyMap.put(TimeoutException.class, new AlwaysRetryPolicy());
        policyMap.put(NullPointerException.class, new NeverRetryPolicy());
        policy.setPolicyMap(policyMap);
        template.setRetryPolicy(policy);
        String result = template.execute(new RetryCallback<String, Exception>() {
            public String doWithRetry(RetryContext arg0) throws Exception {
                if (arg0.getRetryCount() >= 2) {
                    Thread.sleep(1000);
                    throw new NullPointerException();
                }
                throw new TimeoutException("TimeoutException");
            }
        }, new RecoveryCallback<String>() {
            public String recover(RetryContext context) throws Exception {
                return "recovery callback";
            }
        });
        System.out.println(result);
    }

}
CompositeRetryPolicy
用户指定一组策略,随后根据optimistic选项来确认如何重试。
下面的代码中创建CompositeRetryPolicy策略,并创建了RetryPolicy数组,数组有两个具体策略SimpleRetryPolicy与AlwaysRetryPolicy。
当CompositeRetryPolicy设置optimistic为true时,Spring-retry会顺序遍历RetryPolicy[]数组,如果有一个重试策略可重试,例如SimpleRetryPolicy没有达到重试次数,那么就会进行重试。
如果optimistic选项设置为false。那么有一个重试策略无法重试,那么就不进行重试。
例如SimpleRetryPolicy达到重试次数不能再重试,而AlwaysRetryPolicy可以重试,那么最终是无法重试的。
下面代码设置setOptimistic(true),而AlwaysRetryPolicy一直可重试,那么最终可以不断进行重试。
public class RetryTemplate05 {

    public static void main(String[] args) throws Exception {
        RetryTemplate template = new RetryTemplate();

        CompositeRetryPolicy policy = new CompositeRetryPolicy();
        RetryPolicy[] polices = { new SimpleRetryPolicy(), new AlwaysRetryPolicy() };

        policy.setPolicies(polices);
        policy.setOptimistic(true);
        template.setRetryPolicy(policy);

        String result = template.execute(new RetryCallback<String, Exception>() {
            public String doWithRetry(RetryContext arg0) throws Exception {
                if (arg0.getRetryCount() >= 2) {
                    Thread.sleep(1000);
                    throw new NullPointerException();

                }
                throw new TimeoutException("TimeoutException");
            }
        }, new RecoveryCallback<String>() {
            public String recover(RetryContext context) throws Exception {
                return "recovery callback";
            }
        });
        System.out.println(result);
    }

}
ExponentialRandomBackOffPolicy
通过监听器,可以在重试操作的某些位置嵌入调用者定义的一些操作,以便在某些场景触发。
代码注册了两个Listener,Listener中的三个实现方法,onError,open,close会在执行重试操作时被调用,
在RetryTemplate中doOpenInterceptors,doCloseInterceptors,doOnErrorInterceptors会调用监听器对应的open,close,onError方法。
doOpenInterceptors方法在第一次重试之前会被调用,如果该方法返回true,则会继续向下直接,如果返回false,则抛出异常,停止重试。
doCloseInterceptors 会在重试操作执行完毕后调用。
doOnErrorInterceptors 在抛出异常后执行,
当注册多个Listener时,open方法按会按Listener的注册顺序调用,而onError和close则按Listener注册的顺序逆序调用。
public class RetryTemplate06 {

    public static void main(String[] args) throws Exception {
        
        RetryTemplate template = new RetryTemplate();

        ExponentialRandomBackOffPolicy exponentialBackOffPolicy = new ExponentialRandomBackOffPolicy();
        exponentialBackOffPolicy.setInitialInterval(1500);
        exponentialBackOffPolicy.setMultiplier(2);
        exponentialBackOffPolicy.setMaxInterval(6000);

        CompositeRetryPolicy policy = new CompositeRetryPolicy();
        RetryPolicy[] polices = { new SimpleRetryPolicy(), new AlwaysRetryPolicy() };

        policy.setPolicies(polices);
        policy.setOptimistic(true);

        template.setRetryPolicy(policy);
        template.setBackOffPolicy(exponentialBackOffPolicy);

        template.registerListener(new RetryListener() {
            public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                System.out.println("open");
                return true;
            }
            public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                System.out.println("onError");
            }
            public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                System.out.println("close");
            }
        });

        template.registerListener(new RetryListener() {
            public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                System.out.println("open2");
                return true;
            }
            public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                System.out.println("onError2");
            }
            public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                System.out.println("close2");
            }
        });
        String result = template.execute(new RetryCallback<String, Exception>() {
            public String doWithRetry(RetryContext arg0) throws Exception {
                arg0.getAttribute("");
                if (arg0.getRetryCount() >= 2) {
                    throw new NullPointerException();
                }
                throw new TimeoutException("TimeoutException");
            }
        });
        System.out.println(result);
    }

}
有状态RetryTemplate
当把状态放入缓存时,通过该key查询获取,全局模式 DataAccessException进行回滚

public class RetryTemplate07 {
    public static void main(String[] args) throws Exception {
        RetryTemplate template = new RetryTemplate();
        Object key = "mykey";
        boolean isForceRefresh = true;
        BinaryExceptionClassifier rollbackClassifier = new BinaryExceptionClassifier(
                Collections.<Class<? extends Throwable>>singleton(DataAccessException.class));
        RetryState state = new DefaultRetryState(key, isForceRefresh, rollbackClassifier);
        String result = template.execute(new RetryCallback<String, RuntimeException>() {
            @Override
            public String doWithRetry(RetryContext context) throws RuntimeException {
                System.out.println("retry count:" + context.getRetryCount());
                throw new TypeMismatchDataAccessException();
            }
        }, new RecoveryCallback<String>() {
            @Override
            public String recover(RetryContext context) throws Exception {
                return "default";
            }
        }, state);
        System.out.println(result);
    }
}
熔断器场景。在有状态重试时,且是全局模式,不在当前循环中处理重试,而是全局重试模式(不是线程上下文),如熔断器策略时测试代码如下所示:

public class RetryTemplate08 {

    public static void main(String[] args) throws Exception {
        RetryTemplate template = new RetryTemplate();
        CircuitBreakerRetryPolicy retryPolicy = new CircuitBreakerRetryPolicy(new SimpleRetryPolicy(3));
        retryPolicy.setOpenTimeout(5000);
        retryPolicy.setResetTimeout(20000);
        template.setRetryPolicy(retryPolicy);
        for (int i = 0; i < 10; i++) {
            try {
                Object key = "circuit";
                boolean isForceRefresh = false;
                RetryState state = new DefaultRetryState(key, isForceRefresh);
                String result = template.execute(new RetryCallback<String, RuntimeException>() {
                    @Override
                    public String doWithRetry(RetryContext context) throws RuntimeException {
                        System.out.println("retry count:" + context.getRetryCount());
                        throw new RuntimeException("timeout");
                    }
                }, new RecoveryCallback<String>() {
                    @Override
                    public String recover(RetryContext context) throws Exception {
                        return "default";
                    }
                }, state);
                System.out.println(result);
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }

}
XML Configuration
xml配置可以在不修改原来代码的情况下通过,添加spring retry的功能。

@SpringBootApplication
@EnableRetry
@EnableAspectJAutoProxy
@ImportResource("classpath:/retryadvice.xml")
public class XmlApplication {
    public static void main(String[] args) {
        SpringApplication.run(XmlApplication.class, args);
    }
}
public class XmlRetryService {
    public void xmlRetryService(String arg01) throws Exception {
        System.out.println("xmlRetryService do something...");
        throw new RemoteAccessException("RemoteAccessException....");
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd">
    <aop:config>
        <aop:pointcut id="transactional" expression="execution(*XmlRetryService.xmlRetryService(..))" />
        <aop:advisor pointcut-ref="transactional" advice-ref="taskRetryAdvice" order="-1" />
    </aop:config>
    <bean id="taskRetryAdvice" class="org.springframework.retry.interceptor.RetryOperationsInterceptor">
        <property name="RetryOperations" ref="taskRetryTemplate" />
    </bean>
    <bean id="taskRetryTemplate" class="org.springframework.retry.support.RetryTemplate">
        <property name="retryPolicy" ref="taskRetryPolicy" />
        <property name="backOffPolicy" ref="exponentialBackOffPolicy" />
    </bean>
    <bean id="taskRetryPolicy" class="org.springframework.retry.policy.SimpleRetryPolicy">
        <constructor-arg index="0" value="5" />
        <constructor-arg index="1">
            <map>
                <entry key="org.springframework.remoting.RemoteAccessException" value="true" />
            </map>
        </constructor-arg>
    </bean>
    <bean id="exponentialBackOffPolicy"
        class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
        <property name="initialInterval" value="300" />
        <property name="maxInterval" value="30000" />
        <property name="multiplier" value="2.0" />
    </bean>
</beans>
 

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

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

相关文章

Vue+element-china-area-data实现省市区三级联动

安装依赖 npm install element-china-area-data -S cnpm install element-china-area-data -S 引用 import { provinceAndCityData, regionData, provinceAndCityDataPlus, regionDataPlus, CodeToText, TextToCode } from element-china-area-data&#xff1b; provinceAnd…

Linux 命令解释程序(shell)的模拟实现

1.实验内容 分析、设计与实现基于 Linux 内核的命令解释程序&#xff08;Shell&#xff09;&#xff0c;主要包括系统环境变量的设置和初始化、系统命令提示符显示、命令辨别解析&#xff08;区分内部命令与外部命令及不同内部命令&#xff09;、典型内部命令&#xff08;譬如…

写点东西《Docker入门(上)》

写点东西《Docker入门&#xff08;上&#xff09;》 环境变量 Docker 镜像 Docker CMD 与 ENTRYPOINT 有什么区别 Docker 中的网络&#xff1a; Docker 存储&#xff1a; Docker 是一个工具&#xff0c;允许开发人员将他们的应用程序及其所有依赖项打包到一个容器中。然后&…

【Redis】Redis 进阶

文章目录 1. BigKey1.1 MoreKey1.2 BigKey 2. 缓存双写一致性更新策略2.1 读缓存数据2.2 数据库和缓存一致性的更新策略2.3 canal 实现双写一致性 3. 进阶应用3.1 统计应用3.2 hyperloglog3.3 GEO3.4 bitmap 4. 布隆过滤器5. Redis 经典问题5.1 缓存预热5.2 缓存穿透5.3 缓存击…

Android Persistent自启机制

1.persistent属性的使用 在开发系统级的App时&#xff0c;很有可能就会用persistent属性。当在AndroidManifest.xml中将persistent属性设置为true时&#xff0c;那么该App就会具有如下两个特性&#xff1a; 在系统刚起来的时候&#xff0c;该App也会被启动起来 该App被强制杀…

华为完全自研之后,中国的手机会变得更便宜好用吗?

华为完全自研之后&#xff0c;中国的手机是否会变得更便宜好用&#xff0c;这是一个复杂的问题&#xff0c;涉及到多个因素。 首先&#xff0c;华为完全自研意味着公司需要自主研发和生产手机的所有组件&#xff0c;包括处理器、摄像头、屏幕等。这将有助于降低成本&#xff0c…

antd时间选择器,设置显示中文

需求 在实现react&#xff0c;里面引入antd时间选择器&#xff0c;默认显示为英文 思路 入口处使用ConfigProvider全局化配置&#xff0c;设置 locale 属性为中文来实现。官方文档介绍全局化配置 ConfigProvider - Ant Design 代码 import React from react; import { Prov…

道可云元宇宙每日资讯|浙江印发《加快人工智能能产业发展的指导意见》

道可云元宇宙每日简报&#xff08;2024年1月12日&#xff09;讯&#xff0c;今日元宇宙新鲜事有&#xff1a; 浙江印发《加快人工智能能产业发展的指导意见》 近日&#xff0c;浙江省人民政府办公厅印发《加快人工智能能产业发展的指导意见》。 《意见》指出&#xff0c;到 …

(Arcgis)matlab编程批量处理hdf5格式转换为tif格式

国家青藏高原科学数据中心 全球逐日0.05时空连续地表温度数据集&#xff08;2002-2022&#xff09; 此代码仅用于该数据集处理 版本&#xff1a;arcgis10.2 matlab2020 参考&#xff1a;MATLAB hdf(h5)文件转成tif图片格式&#xff08;批量处理&#xff09; 一、遇到问题 h5…

MidTool的AIGC与NFT的结合-艺术创作和版权保护的革新

在数字艺术和区块链技术的交汇点上&#xff0c;NFT&#xff08;非同质化代币&#xff09;正以其独特的方式重塑艺术品的收藏与交易。将MidTool&#xff08;https://www.aimidtool.com/&#xff09;的AIGC&#xff08;人工智能生成内容&#xff09;创作的图片转为NFT&#xff0c…

GEE数据集——2000 年至 2022 年与传感器无关的 MODIS 和 VIIRS LAI/FPAR CDR

2000 年至 2022 年与传感器无关的 MODIS 和 VIIRS LAI/FPAR CDR 该地理空间数据集包含关键的生物物理参数&#xff0c;即叶面积指数&#xff08;LAI&#xff09;和光合有效辐射分量&#xff08;FPAR&#xff09;&#xff0c;是描述陆地生态系统特征不可或缺的参数。该数据集解…

uniapp自定义封装只有时分秒的组件,时分秒范围选择

说实话&#xff0c;uniapp和uview的关于只有时分秒的组件实在是不行。全是日历&#xff0c;但是实际根本就不需要日历这玩意。百度了下&#xff0c;终于看到了一个只有时分秒的组件。原地址&#xff1a;原地址&#xff0c;如若侵犯请联系我删除 <template><view clas…

使用Eexcl调换txt文本中的两列数据

问题描述 本方法使用对txt存储的数据格式有特别要求。需要数据每行具有相同个数数据&#xff0c;且具有统一的间隔符号隔开。&#xff08;常见的间隔符号有tab键、空格、逗号、分号等&#xff09; 对于一个有空格间隔每行只有三列数据的txt文件&#xff0c;对调第二列和第三列…

Linux中关于cat命令详解

cat的作用 用于连接文件并打印到标准输出设备上 cat的参数 -b,-number-nonblank 打印时打印出序号,但不对空白行进行计算-E,--show-ends在每行结束处显示$-n,--number打印时打印出序号-s,--squeeze-blank当遇到有连续两行以上的空白行,就代换为一行的空白行 -T,--show-tabs 将…

为什么要使用云原生数据库?云原生数据库具体有哪些功能?

相比于托管型关系型数据库&#xff0c;云原生数据库极大地提高了MySQL数据库的上限能力&#xff0c;是云数据库划代的产品&#xff1b;云原生数据库最早的产品是AWS的 Aurora。AWS Aurora提出来的 The log is the database的理念&#xff0c;实现存储计算分离&#xff0c;把大量…

C++ n皇后问题 || 深度优先搜索模版题

n− 皇后问题是指将 n 个皇后放在 nn 的国际象棋棋盘上&#xff0c;使得皇后不能相互攻击到&#xff0c;即任意两个皇后都不能处于同一行、同一列或同一斜线上。 现在给定整数 n &#xff0c;请你输出所有的满足条件的棋子摆法。 输入格式 共一行&#xff0c;包含整数 n 。 …

雷达信号处理——恒虚警检测(CFAR)

雷达信号处理的流程 雷达信号处理的一般流程&#xff1a;ADC数据——1D-FFT——2D-FFT——CFAR检测——测距、测速、测角。 雷达目标检测 首先要搞清楚什么是检测&#xff0c;检测就是判断有无。雷达在探测的时候&#xff0c;会出现很多峰值&#xff0c;这些峰值有可能是目标…

C++学习笔记(三十三):c++ 宏定义

本节对c的宏定义进行描述。c使用预处理器来对宏进行操作&#xff0c;我们可以写一些宏来替换代码中的问题&#xff0c;c的宏是以#开头&#xff0c;预处理器会将所有的宏先进行处理&#xff0c;之后在通过编译器进行编译。宏简单说就是文本替换&#xff0c;可以替换代码中的任何…

swarm节点间通信问题-关闭checksum校验和

现场还原 客户有n台redhat虚拟机&#xff0c;构建了一个swarm集群&#xff0c;服务起来后&#xff0c;发现不同节点间的服务&#xff0c;无法互相访问。经运维大佬排查是服务器的checksum校验是开启状态&#xff0c;关闭即可~ 查看checksum状态 ethtool -k 内网网卡名称 | g…

Rhinoceros 8(犀牛8)中文授权版支持Win/Mac

Rhinoceros 8&#xff0c;也称为犀牛8&#xff0c;是一款专业的三维建模软件&#xff0c;深受设计师们的喜爱。这款软件为设计师提供了无限的创意空间和强大的工具&#xff0c;无论他们是产品设计师、建筑师还是工业设计师。 Rhinoceros 8采用了先进的NURBS建模技术&#xff0c…