Flink实战五_状态机制

接上文:Flink实战四_TableAPI&SQL

在学习Flink的状态机制之前,我们需要理解什么是状态。回顾我们之前介绍的很多流计算的计算过程,有些计算方法,比如说我们之前多次使用的将stock.txt中的一行文本数据转换成Stock股票对象的map操作。来一个数据,就计算一个数据,这些操作,只需要依赖于当前输入的数据就够了,不需要其他的辅助数据。输入相同的文本数据,输出的肯定是一个相同的Stock股票对象。

而另外一些操作,比如在WindowFunctionDemo中介绍的计算平均值的方法。同样是来一个数据处理一个数据,但是,他每次计算出来的结果,除了依赖于当前输入的数据,还需要依赖于accumulate累加器中的数据。输入相同的股票数据,由于累加器中的数据不同,输出的股票平均价格也就不同。

像累加器这种由一个任务来维护,并且要参与到数据计算过程中的数据,就称为状态。这一类计算任务,也称为有状态的任务。比如reduce、sum、min、minby等等操作,都是典型的有状态的算子。而与之对应的,只依赖于输入数据的计算任务,就称为无状态的任务。多个任务叠加在一起,就组成了一个客户端应用。

对于状态,也可以认为就是一个本地变量,他可以被一个客户端应用中的所有计算任务都访问到。对于状态的管理通常是比较复杂的,尤其在分布式流式计算场景下。任务是并行计算的,所以状态也需要分开保存。集群故障恢复后又需要合并读取。在算子并行度发生变化时又要维护状态的一致性。再考虑到状态数据要尽量高效的存储与访问,等等。Flink的状态机制提供了对这类状态数据的统一管理。开发人员可以专注于开发业务逻辑,而不用时刻考虑状态的各种复杂管理机制。

对于状态,有两种管理机制,一种是managed state,就是Flink管理的状态机制,对之前提到的一些状态管理的问题提供了统一的管理机制。另一种是raw state,就是用户自己管理的状态机制。只需要Flink提供一个本地变量空间,由应用程序自己去管理这一部分状态。Flink的状态管理机制非常强大,所以在大部分的开发场景下,我们使用Flink提供的状态管理机制就足够了。

Flink中管理的状态都是跟特定计算任务关联在一起的。他的状态主要有两种,一种是operator state 算子状态,一种是keyed State 键控状态。

1、Operator State 算子状态

算子状态的作用范围限定为当前计算任务内,这种状态是跟一个特定的计算任务绑定的。算子状态的作用范围只限定在算子任务内,由同一并行任务所处理的所有数据都可以访问到相同的状态。并且这个算子状态不能由其他子任务访问。比如WindowFunctionDemo中计算股票平均价格的MyAvg计算任务里的累加器,就只能在当前计算任务中访问。即使在多个不同的应用程序中都可以使用MyAvg这个计算任务,但是每个应用程序中访问到的累加器都是不同的。

这一类算子需要按任务分开保存,而当任务的并行度发生变化时,还需要支持在并行运算实例之间,重新分配状态。

例如下面我们定义一个带状态的求和算子,在这个示例中就给一个简单的求和算子保存了一个状态。

示例代码 : com.flink.state.SumOperatorState

import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.state.ListState;
import org.apache.flink.api.common.state.ListStateDescriptor;
import org.apache.flink.api.common.typeinfo.TypeHint;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.contrib.streaming.state.RocksDBStateBackend;
import org.apache.flink.runtime.state.FunctionInitializationContext;
import org.apache.flink.runtime.state.FunctionSnapshotContext;
import org.apache.flink.streaming.api.checkpoint.CheckpointedFunction;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

/**
 * 算子状态
 */
public class SumOperatorState {
    public static void main(String[] args) throws Exception {
        final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(2);
        env.setStateBackend(new RocksDBStateBackend("hdfs://hadoop01:8020/SumOperatorState"));
//        env.setRestartStrategy(RestartStrategies.fixedDelayRestart(3, Time.of(10, TimeUnit.SECONDS)));
        final DataStreamSource<Integer> stream = env.fromElements(1, 2, 3, 4, 5,6);
        final SingleOutputStreamOperator<Integer> stream2 = stream.map(new MySumMapper("mysummapper"));
        stream2.print();
//        final DataStream<Integer> union = stream.union(stream2);
        env.execute("stream");
    }

    public static class MySumMapper implements MapFunction<Integer,Integer>, CheckpointedFunction {

        private int sum;
        private String stateKey;
        private ListState<Integer> checkpointedState;

        public MySumMapper(String stateKey){
            this.stateKey = stateKey;
        }

        @Override
        public Integer map(Integer value) throws Exception {
            return sum += value;
        }

        @Override
        public void snapshotState(FunctionSnapshotContext context) throws Exception {
            checkpointedState.clear();
            checkpointedState.add(sum);
        }

        @Override
        public void initializeState(FunctionInitializationContext context) throws Exception {
            ListStateDescriptor<Integer> descriptor = new ListStateDescriptor<Integer>(
                    stateKey,
                    TypeInformation.of(new TypeHint<Integer>() {
            }));
            checkpointedState = context.getOperatorStateStore().getListState(descriptor);
            if(context.isRestored()){
                for (Integer subSum : checkpointedState.get()) {
                    sum += subSum;
                }
            }
        }
    }
}

可以看到,Flink中的算子状态操作还是比较简单的,可以给算子继承一个CheckpointedFunction接口,这个接口有两个方法,snapshotState方法会在算子执行过程中调用,进行状态保存。initializeState方法是在任务启动时加载初始化的状态。这样,算子在执行过程中,就可以将中间结果保存到checkpointedState状态中。当算子异常终止时,下一次启动又可以从这个checkpointedState状态中加载之前的计算结果。用户程序只需要定义逻辑,而不需要管触发的时机。

关于不同的状态类型:
在获取状态的地方: context.getOperatorStateStore()这个方法有几个重载的方法:getListState,getUnionListState,getBroadcastState。

其中,getListState和getUnionListState,这两个方法都是处理ListState,也就是不同的任务节点,他的状态也不相同。只是这两种状态的底层状态分配机制不同。ListState是将不同的子状态分配好了之后,分给不同的算子实例去处理。而
UnionListState则是将所有的子状态都分配给所有的算子实例,由算子实例自行调节每个实例获取哪些状态。FlinkKafkaConsumer就是使用的UnionListState。

最后一个getBoradcastState,是处理广播状态,也就是所有任务节点的状态都是一样的。

其他的算子,包括function,source,sink都可以自行添加状态管理。这其中需要理解的就是checkpointedState的形式,为什么是一个集合状态ListState?

在这里插入图片描述
这是因为Flink的计算任务都是并行执行的,那么在计算过程中,每一个并行的实例都会有一个自己的状态,所以在snapshotState保存状态时,是将每个并行实例内的状态进行保存,那整个任务整体就会保存成一个集合。所以,示例中保存的其实是每个子任务内计算到的sum和。

当任务重新启动时,Flink可能还需要对子任务的状态进行重新分配,因为任务的并行度有可能进行了调整。所以示例中initializeState方法加载状态时,也是将各个子状态的sum加到一起,才是一个完整的求和计算。

2、keyed State 键控状态

算子状态针对的是普通算子,在任何DataStream和DataSet中都可以使用。但是,如果针对KeyedStream,情况又有所不同。相比算子状态,keyedState键控状态是针对keyby产生的KeyedStream。KeyedStream的计算任务都跟当前分配的key直接关联。相对应的KeyedState状态也就跟key有关。而key是在计算任务运行时分配的。这一类状态,无法在任务启动过程中完成状态的分配。需要在任务执行过程中,根据key的分配不同而进行不同的分配。Flink针对keyedStream,会在内部根据每个key维护一个键控状态。在具体运算过程中,根据key的分配情况,将状态分配给不同的计算任务。

针对键控状态, Flink提供了一系列Rich开头的富计算因子抽象类,这些抽象类提供了更丰富的计算任务生命周期管理。用户程序通过继承这些抽象类,就可以获取到与当前分配的key相关的状态。

我们先来看一个关于KeyedStream的状态示例。下面实现了一个自定义的求word count的算子。

示例代码 : com.flink.state.WCKeyedState

import org.apache.flink.api.common.functions.FlatMapFunction;
import org.apache.flink.api.common.functions.RichFlatMapFunction;
import org.apache.flink.api.common.state.StateTtlConfig;
import org.apache.flink.api.common.state.ValueState;
import org.apache.flink.api.common.state.ValueStateDescriptor;
import org.apache.flink.api.common.time.Time;
import org.apache.flink.api.common.typeinfo.TypeHint;
import org.apache.flink.api.common.typeinfo.TypeInformation;
import org.apache.flink.api.java.DataSet;
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.api.java.operators.DataSource;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.util.Collector;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author roy
 * @date 2021/9/6
 */
public class WCKeyedState {
    public static void main(String[] args) throws Exception {
        //创建执行环境
        final StreamExecutionEnvironment environment = StreamExecutionEnvironment.getExecutionEnvironment();

        final DataStreamSource<Tuple2<String, Integer>> stream = environment.fromCollection(Arrays.asList(Tuple2.of("a", 1), Tuple2.of("a", 5), Tuple2.of("a", 7), Tuple2.of("a", 2)
                , Tuple2.of("b", 2), Tuple2.of("b", 6), Tuple2.of("b", 3), Tuple2.of("b", 8)
                , Tuple2.of("c", 4), Tuple2.of("c", 8), Tuple2.of("c", 4), Tuple2.of("c", 6)));
        //按照字符分组
        final KeyedStream<Tuple2<String, Integer>, String> keyedStream = stream.keyBy((key) -> key.f0);
        keyedStream.flatMap(new WCFlatMapFunction("WCKeyedState")).print();

        environment.execute("WCKeyedState");
    }

    public static class WCFlatMapFunction extends RichFlatMapFunction<Tuple2<String,Integer>, Tuple2<String,Integer>>{

        private String stateDesc;
        ValueState<Tuple2<String, Integer>> valueState;

        public WCFlatMapFunction(String stateDesc) {
            this.stateDesc = stateDesc;
        }

        @Override
        public void flatMap(Tuple2<String, Integer> input, Collector<Tuple2<String, Integer>> out) throws Exception {
            Tuple2<String, Integer> wordCountList = valueState.value();
            if(null == wordCountList){
                wordCountList = input;
            }else{
                wordCountList.f1+= input.f1;
            }
            valueState.update(wordCountList);
            out.collect(wordCountList);
//            valueState.clear();
        }

        @Override
        public void open(Configuration parameters) {
            ValueStateDescriptor<Tuple2<String,Integer>> descriptor =
                new ValueStateDescriptor<>(stateDesc,
                        TypeInformation.of(new TypeHint<Tuple2<String, Integer>>() {}));
            //设置状态的存活时间
            StateTtlConfig ttlConfig = StateTtlConfig
                    .newBuilder(Time.seconds(1))
                    .setUpdateType(StateTtlConfig.UpdateType.OnCreateAndWrite)
                    .setStateVisibility(StateTtlConfig.StateVisibility.NeverReturnExpired)
                    .build();
            descriptor.enableTimeToLive(ttlConfig);
            valueState = this.getRuntimeContext().getState(descriptor);
            //另外几种state类型
//            this.getRuntimeContext().getMapState();
//            this.getRuntimeContext().getListState();
//            this.getRuntimeContext().getAggregatingState();
//            this.getRuntimeContext().getReducingState();
        }

    }
}

在这个示例中看到,其实键控状态与算子状态在应用代码层面最大的区别在于获取状态的方法。算子状态可以通过FunctionInitializationContext直接拿到状态,而键控状态需要实现Rich***Function接口,在open方法中通过getRuntimeContext()获取。

而更深层次的区别在于运行机制上。其实你可以把这个状态近似的理解为一个
(key,value)结构的本地缓存。算子状态的缓存是一个固定的key,只是这个key跟当前计算任务有关,只有当前这一个算子能够读到,不管在哪个taskmanager上计算,如果能读到这个缓存的话,那读到的缓存就是一个固定的。而键控状态的缓存是一组(key,value)的缓存,这一组缓存的key就是KeyedStream中的key分区键。而键控状态获取到的状态值都是取决于当前输入元素所代表的key分区键的,因此,每次任务时taskmanager上分配的key不同,那就可能读取到不同的值。

另外,根据状态类型不同, Flink也提供了几种不同的状态:

  • ValueState: 保存一个可以更新和检索的值。 这个值可以通过 update(T) 进行更新,通过 T value() 进行检索。
  • ListState: 保存一个元素的列表。可以往这个列表中追加数据,并在当前的列表上进行检索。可以通过 add(T) 或者 addAll(List) 进行添加元素,通过Iterable get() 获得整个列表。还可以通过 update(List) 覆盖当前的列表。
  • ReducingState: 保存一个单值,表示添加到状态的所有值的聚合。接口与ListState 类似,但使用 add(T) 增加元素,会使用提供的ReduceFunction 进行聚合。
  • AggregatingState: 保留一个单值,表示添加到状态的所有值的聚合。和ReducingState 相反的是, 聚合类型可能与 添加到状态的元素的类型不同。 接口与 ListState 类似,但使用 add(IN) 添加的元素会用指定的AggregateFunction 进行聚合。
  • MapState: 维护了一个映射列表。 你可以添加键值对到状态中,也可以获得反映当前所有映射的迭代器。使用 put(UK,UV) 或者 putAll(Map) 添加映射。使用 get(UK) 检索特定 key。 使用 entries(),keys() 和 values() 分别检索映射、键和值的可迭代视图。你还可以通过 isEmpty() 来判断是否包含任何键值对。

这些不同的状态都是跟Key相关的。使用时,都需要通过构建一个对应的
StateDescriptor,然后通过getRuntimeContext获取。

3、Checkpointing 检查点

Flink中的每个算子都可以是有状态的,这些状态化的方法和算子可以使Flink的计算过程更为精确,在实际开发中,应该尽量使用带状态的算子。而对于这些状态,除了可以通过算子状态和键控状态进行扩展外,Flink也提供了另外一种自动的兜底机制,CheckPointing检查点。

Checkpointing检查点是一种由Flink自动执行的一种状态备份机制,其目的是能够从故障中恢复。快照中包含了每个数据源Source的指针(例如,到文件或者kafka分区的偏移量)以及每个有状态算子的状态副本。

默认情况下,检查点机制是禁用的,需要在应用中通过StreamExecutionEnvironment 进行配置。基础的配置方式是通过StreamExecutionEnvironment的enableCheckpointing方法开启,开启时需要传入一个参数,表示多长时间执行一次快照。另外有一些高级的选项,可以参见下面的示例。

StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
// 每 1000ms 开始一次 checkpoint
env.enableCheckpointing(1000);

// 高级选项:
// 设置模式为精确一次 (这是默认值)
env.getCheckpointConfig().setCheckpointingMode(CheckpointingMode.EXACTLY_ON
CE);
// 确认 checkpoints 之间的时间会进行 500 ms
env.getCheckpointConfig().setMinPauseBetweenCheckpoints(500);
// Checkpoint 必须在一分钟内完成,否则就会被抛弃
env.getCheckpointConfig().setCheckpointTimeout(60000);
// 同一时间只允许一个 checkpoint 进行
env.getCheckpointConfig().setMaxConcurrentCheckpoints(1);
// 开启在 job 中止后仍然保留的 externalized checkpoints
env.getCheckpointConfig().enableExternalizedCheckpoints(ExternalizedCheckpo
intCleanup.RETAIN_ON_CANCELLATION);

4、Flink的容错重启机制

当某一个task发生故障时,Flink需要重启出错的task以及其他收到影响的Task,以使得作业恢复到正常执行的状态。Flink通过重启策略和故障恢复策略来控制Task重启:重启策略决定是否可以重启以及重启的间隔;故障恢复策略决定哪些Task需要重启。

重启策略可以通过配置文件flink-conf.yaml中通过restart-strategy属性进行配置,同样,也可以在应用程序中覆盖配置文件中的配置。如果没有启用checkpoint,那就采用"不重启"的策略。如果启用了checkpoint并且没有配置重启策略,那么就采用固定延时重启策略,这种情况下最大尝试重启次数是Integer.MAX_VALUE,基本就可以认为是会不停的尝试重启。

restart-strategy属性可选的配置有以下几种:

  • none 或 off 或 disable: 不重启。checkpointing关闭后的默认值
  • fixeddelay, fixed-delay: 固定延迟重启策略。checkpointing启用时的默认值
  • failurerate, failure-rate: 失败率重启策略

这些配置项同样可以在应用程序中定制。例如

ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(
3, // 尝试重启的次数
Time.of(10, TimeUnit.SECONDS) // 延时
));

fixeddelay策略,还可以定制两个参数,restart-strategy.fixed-delay.attempts 重试次数以及 restart-strategy.fixed-delay.delay延迟时间。第一个参数表示重启任务的尝试次数,第二个参数表示重启失败后,再次尝试重启的间隔时间。可以配置为 “1 min”,"20 s"这样。 例如在配置文件中

restart-strategy.fixed-delay.attempts: 3
restart-strategy.fixed-delay.delay: 10 s

或者在应用程序中

ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
env.setRestartStrategy(RestartStrategies.fixedDelayRestart(
	3, // 尝试重启的次数
	Time.of(10, TimeUnit.SECONDS) // 延时
));

Failure Rate 策略表示当故障率(每个时间假根发生故障的次数)超过设定的限制时,作业就会最终失败。 在连续的两次重启尝试之间,重启策略会等待一段固定长度的时间。

这种策略下,可以定义三个详细的参数。

  • restart-strategy.failure-rate.max-failures-per-interval: 任务失败之前,在固定时间间隔内的最大重启尝试次数。
  • restart-strategy.failure-rate.failure-rate-interval: 检测失败率的窗口间隔。
  • restart-strategy.failure-rate.delay 两次重启尝试之间的间隔时间。

例如在配置文件中

restart-strategy: failure-rate
restart-strategy.failure-rate.max-failures-per-interval: 3
restart-strategy.failure-rate.failure-rate-interval: 5 min
restart-strategy.failure-rate.delay: 10 s

或者在应用中

ExecutionEnvironment env = ExecutionEnvironment.getExecutionEnvironment();
env.setRestartStrategy(RestartStrategies.failureRateRestart(
	3, // 每个时间间隔的最大故障次数
	Time.of(5, TimeUnit.MINUTES), // 测量故障率的时间间隔
	Time.of(10, TimeUnit.SECONDS) // 延时
));

5、State Backend 状态存储方式与位置

通过算子状态,键控状态以及检查点,我们可以对计算过程中的中间状态进行保存。这些保存下来的状态即可以在计算中使用,也可以在计算程序异常终止后恢复计算状态时使用。但是,到目前为止,我们都是直接拿来用,而并没有去关注这些状态数据是以何种方式保存并且是保存在什么地方的。

针对这些状态,Flink提供了多种State Backend 状态后端,用来管理状态数据具体的存储方式与位置。Flink默认提供了三种状态后端:jobmanager,filesystem,rocksdb。设置的方式可以在file-conf.yaml中,通过state.backend属性进行配置。也可以在程序中通过StreamExecutionEnvironment配置。例如:

StreamExecutionEnvironment env =
StreamExecutionEnvironment.getExecutionEnvironment();
env.setStateBackend(...);

另外,在flink-conf.yaml中,关于state.backend还有一些扩展的属性,这些属性同样即可以在配置文件中配置,也可以在程序中设置。应用程序中的配置优先级更高。

那这三种状态后端要如何取舍呢?

  • jobmanager:
    jobmanager在后台由一个MemoryStateBackend类来实现,从名字看出,是基于内存实现的。状态信息会保存到TaskManager的JVM堆内存中,而检查点信息则会直接保存到JobManager的内存中。这些检查点信息虽然都是基于内存工作,但是也依然会持久化到文件系统当中。

由于检查点保存在Jobmanager中,会加大taskmanager和jobmanager之间的
网络请求,并且也会加大jobmanager的负担,所以这种方式通常只用于实验场
景或者小状态的本地计算场景。

  • filesystem:
    filesystem在后台由一个FsStateBackend类来实现。他依然是基于内存和文件系统进行状态保存。但是检查点信息是由taskmanager进行保存的。保存的文件地址是可以自行配置的。由于taskmanager上执行的任务是动态分配的,所以通常这个保存地址需要配置成所有taskmanager都能访问到的地方,例如hdfs。而taskmanager上由于会有多个并行任务,所以他们的文件存储地址也会用数字进行版本区分,例如hdfs://namenode:port/flink-checkpoints/chk-17/.filesystem的状态访问很快速,适合那些需要大的堆内存的场景。但是fliesystem是受限于内存和GC的,所以他支持的状态数据大小优先。

  • rocksdb:
    rocksdb在后台是由一个 RocksDBStateBackend 类来实现的。RocksDB是一个访问快速的key-value本地缓存,你可以把他理解为一个本地的Redis。但是他能够基于文件系统提供非常高效的访问。所以是一个非常常用的流式计算持久化工具。使用RocketDB后,状态数据就不再受限于内存,转而受限于硬盘RocketDBStateBackend适合支持非常大的状态信息存储。但是RocksDB毕竟是基于文件系统的,所以他的执行速度会比filesystem稍慢,官方提供的经验是大概比filesystem慢10倍,但是这个速度在大多数场景下,也依然够用了。

注:如果在应用中使用rocksdb,需要引入一个依赖

<dependency>
	<groupId>org.apache.flink</groupId>
	<artifactId>flink-statebackend-rocksdb_2.12</artifactId>
	<version>${flink.version}</version>
</dependency>

然后使用StreamExecuteEnvironment设置

env.setStateBackend(new RocksDBStateBackend("key"));

章节总结
在流式计算的场景下,应用程序通常是无法预知数据何时到来的,只能一直运行随时等待数据接入。这时一旦应用程序突然出错终止,就很容易造成数据丢失。所以在流式计算场景下,我们需要对程序的健壮性做更多的考量。Flink提供了一系列的状态机制来加强程序的健壮性。但是在重要的生产环境中,我们对程序健壮性做再多的考量都是不过分的,因此通常还需要加上一些基于运维的监控机制,例如监控flink的进程,监控yarn中的任务状态等,来了进一步保证流式计算程序的安全。

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

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

相关文章

Mongodb安装Linux

确定你的CentOS 版本 使用以下命令: /etc/centos-release下载Mongodb 解压tgz压缩包 创建data和log文件夹 , 确定你的文件夹访问权限 在log文件夹里面创建mongodb.log文件(这一步很重要 ! ! !) touch mongodb.log创建mongodb.conf文件 在你的mongodb文件夹下 vi mongo…

IDEA的properties默认编码是UTF-8但是不显示中文

问题描述 今天打开IDEA项目&#xff0c;发现messages_zh_CN.properties不显示中文了 但奇怪的是target下的文件就是展示的中文 而且我IDEA已经配置了编码格式是UTF-8了 使用nodepad打开源文件&#xff0c;也是展示编码格式是UTF-8 &#xff08;打开target下的文件&#xff0c;…

数字存内计算与云边端具有广泛的应用场景深度剖析【根据中国移动研究院文献分析总结】

文章目录 背景数字存内计算技术研究端侧应用场景边侧应用场景云侧应用场景 总结参考文献&#xff1a; 背景 存内计算产品基于其不同的器件特性和计算方式&#xff0c;能够为云、边缘和端设备提供推理、训练等多种人工智能&#xff08;AI&#xff09;能力&#xff0c;从而提升运…

vue3/vue2中自定义指令不可输入小数点.

import { directive } from vueconst noDecimal {mounted(el) {el.addEventListener(keypress, (e) > {if (e.key .) {e.preventDefault() }})} }// 使用自定义指令 directive(no-decimal, noDecimal)使用 标签上添加 v-no-decimal <el-input…

设计模式_备忘录模式_Memento

案例引入 游戏角色有攻击力和防御力&#xff0c;在大战Boss前保存自身的状态(攻击力和防御力)&#xff0c;当大战Boss后攻击力和防御力下降&#xff0c;可以从备忘录对象恢复到大战前的状态 传统设计方案 针对每一种角色&#xff0c;设计一个类来存储该角色的状态 【分析】…

如何使用 Supabase Auth 在您的应用程序中设置身份验证

在本文中&#xff0c;您将学习基本的关键概念&#xff0c;这些概念将帮助您掌握身份验证和授权的工作原理。 您将首先了解什么是身份验证和授权&#xff0c;然后了解如何使用 Supabase auth 在应用程序中实现身份验证。 &#xff08;本文内容参考&#xff1a;java567.com&…

【Linux取经路】进程控制——进程等待

文章目录 一、进程创建1.1 初识 fork 函数1.2 fork 函数返回值1.3 写时拷贝1.4 fork 的常规用法1.5 fork 调用失败的原因1.6 创建一批进程 二、进程终止2.1 进程退出场景2.2 strerror函数2.3 errno全局变量2.4 程序异常2.5 进程常见退出方法2.6 exit 函数2.7 _exit 函数和 exit…

mysql8安装基础操作(一)

一、下载mysql8.0 1.查看系统glibc版本 这里可以看到glibc版本为2.17&#xff0c;所以下载mysql8.0的版本时候尽量和glibc版本对应 [rootnode2 ~]# rpm -qa |grep -w glibc glibc-2.17-222.el7.x86_64 glibc-devel-2.17-222.el7.x86_64 glibc-common-2.17-222.el7.x86_64 gl…

【Tomcat与网络9】提高Tomcat启动速度的八大措施

本文我们来看一下如何对Tomcat进行调优&#xff0c;我们对于Tomcat的调优主要集中在三个方面&#xff1a;提高启动速度、提高系统稳定性和提高并发能力&#xff0c;后两者很多时候是相辅相成的&#xff0c;我们放在一起看。 Tomcat现在一般都嵌入在SpringBoot里&#xff0c;因…

基于ecal的foxglove studio可视化工具的使用

ecal通讯在自动驾驶和机器人中的应用越来越多,在调试测试过程中,可以使用ecal monitor,ecal recoder和ecal player等工具,对ecal 消息进行监测录制回播。但是,有时候需要对消息进行可视化查看,比如雷达点云信息,相机图像等,可以使用foxglove studio可视化工具。 Foxg…

高性能跨平台网络通信框架 HP-Socket v6.0.1

项目主页 : http://www.oschina.net/p/hp-socket开发文档 : https://www.docin.com/p-4592706661.html下载地址 : https://github.com/ldcsaa/HP-SocketQQ Group: 44636872, 663903943 v6.0.1 更新 一、主要更新 优化Linux通信组件多路复用处理架构&#xff0c;避免“惊群”问…

Android 高德地图切换图层

一、默认样式 Android 地图 SDK 提供了几种预置的地图图层&#xff0c;包括卫星图、白昼地图&#xff08;即最常见的黄白色地图&#xff09;、夜景地图、导航地图、路况图层。 findViewById<TextView>(R.id.normal).setOnClickListener {updateSelectedStatus(TYPE_NORMA…

基于SpringBoot+Vue实现的物流快递仓库管理系统

基于SpringBootVue实现的物流快递仓库管理系统 文章目录 基于SpringBootVue实现的物流快递仓库管理系统系统介绍技术选型成果展示账号地址及其他说明源码获取 系统介绍 系统演示 关注视频号【全栈小白】&#xff0c;观看演示视频 基于SpringBootVue实现的物流快递仓库管理系…

【PyQt】02-基本UI

文章目录 前言一、首先了解什么是GUI&#xff1f;二、初学程序1.界面展示代码运行结果 2.控件2.1按钮展示代码运行结果 2.2 纯文本和输入框代码运行结果 3、重新设置大小 -resize4、移动窗口-move()5、设置界面在电脑中央5.1 代码运行结果 6、设置窗口图标代码运行结果 7、布局…

上岸秘籍来啦!TOGAF认证考试全攻略

上岸秘籍来啦&#xff01;手把手教你如何顺利通过TOGAF认证考试&#xff01; &#x1f31f;考试内容 TOGAF 9.2认证分为两个级别&#xff1a; ✅ TOGAF基础级&#xff1a;掌握标准术语、结构和基本概念&#xff0c;理解企业架构和核心标准。 ✅ TOGAF鉴定级&#xff1a;深入分析…

Skywalking trace UI页面中字段信息详解,包括端点、跨度类型和Peer

刚上手Skywalking的同学可能对 trace UI 页面信息中的字段信息不是很了解&#xff0c;在这里就给大家一一讲解&#xff0c;重点关注端点、跨度类型和Peer 服务 :服务的名称 实例&#xff1a;服务对应的实例 端点&#xff1a;端点(Endpoint) 对于特定服务所接收的请求路径, 如…

2024技术发展洞察与趋势学习总结

2023技术发展洞察 2024技术发展趋势

算法--数论

这里写目录标题 质数&#xff08;素数&#xff09;定义判断是否为质数暴力写法&#xff0c;试除法基本思想具体写法 优化基本思想&#xff08;时间复杂度根号n&#xff09;具体写法 分解质因数分析题意暴力写法基本思想具体代码 优化基本思想&#xff08;时间复杂度小于等于根号…

【GEE】基于GEE可视化和下载Landsat8 L2A数据(镶嵌、裁剪)

之前发过一篇使用GEE下载Landsat8的文章&#xff0c;然后有很多小伙伴私信我各种问题&#xff0c;如L1C、L2数据代码怎么修改&#xff0c;如何镶嵌&#xff0c;如何去云、 如何裁剪等一系列问题。正好快过年了&#xff0c;手头的事也没有多少了&#xff0c;所以这两天整理了一下…

解决PS图片打印不清楚(锐化)

解决PS图片打印不清楚&#xff08;锐化&#xff09; 操作如下&#xff1a;