Flink从入门到实践(二):Flink DataStream API

news2024/12/31 4:52:07

文章目录

  • 系列文章索引
  • 三、DataStream API
    • 1、官网
    • 2、获取执行环境(Environment)
    • 3、数据接入(Source)
      • (1)总览
      • (2)代码实例(1.18版本已过时的)
      • (3)使用Source接口
    • 4、数据处理(Transformation)
      • (1)总览
      • (2)Map(后续用该结果测试)
      • (3)Filter
      • (4)FlatMap
      • (5)KeyBy
      • (6)Reduce
      • (7)Union
      • (8)Connect
      • (9)CoMap, CoFlatMap
      • (10)Physical Partitioning 分区
      • (11)Side Outputs 分流操作
    • 5、数据输出(Data Sinks、Connectors)
      • (1)总览
      • (2)print
      • (3)自定义Sink打印到控制台
      • (4)写入到文件
      • (5)输出到MySQL
      • (6)输出到Redis
      • (7)输出到Socket
      • (8)输出到Kafka

系列文章索引

Flink从入门到实践(一):Flink入门、Flink部署
Flink从入门到实践(二):Flink DataStream API
Flink从入门到实践(三):数据实时采集 - Flink MySQL CDC

三、DataStream API

1、官网

https://nightlies.apache.org/flink/flink-docs-release-1.18/docs/dev/datastream/overview/

2、获取执行环境(Environment)

/**
 * 1、获取Flink执行的环境
 * getExecutionEnvironment() 这是我们用的最多的一种
 * createLocalEnvironment()  这种仅限于本地开发使用
 * createRemoteEnvironment(String host, int port, String... jarFiles);  知道就行,开发不用
 *
 *
 * getExecutionEnvironment 传入一个 new Configuration(),本质上是一个HashMap
 */
// StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(new Configuration());
final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env.enableCheckpointing(3000); // 3秒检查一次,提高应用程序的容错性和数据一致性。
DataStream<String> text = env.readTextFile("file:///path/to/file");

通常来说使用getExecutionEnvironment()就可以了,会自动选择你当前的运行环境。

3、数据接入(Source)

(1)总览

我们可以使用env.addSource(sourceFunction)来添加数据来源,实际有许多内置的Source,也可以定义自己的Source。

如果想要自定义数据来源,比如说(该方式在1.18已过时,推荐使用Source接口):
实现SourceFunction接口来实现单并行度的数据来源;
实现ParallelSourceFunction接口来实现多并行度的数据来源;
实现RichParallelSourceFunction接口来实现更高级的多并行度的数据来源。

内置的数据来源(本质上也是使用env.addSource(sourceFunction)来已经预实现了):
env.readTextFile(path):逐行读取文本文件,即符合TextInputFormat规范的文件,并将其作为字符串返回。
readFile(fileInputFormat, path):按照指定的文件输入格式读取(一次)文件。
readFile(fileInputFormat, path, watchType, interval, pathFilter, typeInfo):更加复杂的文件处理。
socketTextStream():从Socket读取。元素可以用分隔符分隔。
fromCollection(Collection)fromCollection(Iterator, Class)fromElements(T ...)fromParallelCollection(SplittableIterator, Class)generateSequence(from, to):从集合读取。
addSource(new FlinkKafkaConsumer<>(...)):从kafka读取。

在这里插入图片描述

(2)代码实例(1.18版本已过时的)

// 实体类
public class Access {

    private long time;
    private String domain;
    private double traffic;
}

public class Student {

    private int id;
    private String name;
    private int age;
}
// 工具类 需要引入mysql-connector-java包
import java.sql.Connection;
import java.sql.DriverManager;

public class MySQLUtils {

    public static Connection getConnection() throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/flink", "root", "123");
    }

    public static void close(AutoCloseable closeable) {
        if(null != closeable) {
            try {
                closeable.close(); // null.close
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                closeable = null;
            }
        }
    }
}

// 自定义source
/**
 * 自定义数据源
 * 并行度为1
 */
public class AccessSource implements SourceFunction<Access>{

    volatile boolean isRunning = true;

    /**
     * 造数据是自定义数据源的使用方式之一
     * @param ctx
     * @throws Exception
     */
    @Override
    public void run(SourceContext<Access> ctx) throws Exception {

        Random random = new Random();
        String[] domains = {"test1.com","test2.com","test3.com"};
        while (isRunning) {
            long time = System.currentTimeMillis();
            ctx.collect(new Access(time, domains[random.nextInt(domains.length)], random.nextInt(1000) + 1000));

            Thread.sleep(2000);
        }
    }

    @Override
    public void cancel() {
        isRunning = false;
    }
}

/**
 * 自定义数据源
 * 多并行度
 */
public class AccessSourceV2 implements ParallelSourceFunction<Access> {

    volatile boolean isRunning = true;

    /**
     * 造数据是自定义数据源的使用方式之一
     * @param ctx
     * @throws Exception
     */
    @Override
    public void run(SourceContext<Access> ctx) throws Exception {

        Random random = new Random();
        String[] domains = {"test1.com","test2.com","test3.com"};
        while (isRunning) {
            long time = System.currentTimeMillis();
            ctx.collect(new Access(time, domains[random.nextInt(domains.length)], random.nextInt(1000) + 1000));

            Thread.sleep(5000);
        }
    }

    @Override
    public void cancel() {
        isRunning = false;
    }
}

/**
 * RichSourceFunction: Rich  +   SourceFunction
 * Rich: 包含了生命周期方法  open  close
 * SourceFunction:单
 *
 * 自定义二次开发:按照框架(Flink/Spark/....)所提供的接口,去实现自己的业务逻辑即可
 * 自定义Source
 * 自定义Sink
 *
 *
 * 扩展:对于Spark SQL的外部数据源熟悉吗? 按照Spark所提供的接口,自己实现业务逻辑
 *
 */
public class MySQLSource extends RichSourceFunction<Student> {


    Connection connection;
    PreparedStatement pstmt;

    /**
     * 初始化操作,建立connection
     */
    @Override
    public void open(Configuration parameters) throws Exception {
        connection = MySQLUtils.getConnection();
        pstmt = connection.prepareStatement("select * from student");
    }

    /**
     * 释放资源,关闭connection
     */
    @Override
    public void close() throws Exception {
        MySQLUtils.close(pstmt);
        MySQLUtils.close(connection);

    }

    /**
     * 业务逻辑:就是把表中的数据读取出来 ==> Student
     */
    @Override
    public void run(SourceContext<Student> ctx) throws Exception {
        ResultSet rs = pstmt.executeQuery();
        while (rs.next()) {
            int id = rs.getInt("id");
            String name = rs.getString("name");
            int age = rs.getInt("age");
            Student student = new Student(id, name, age);
            ctx.collect(student);
        }
    }

    @Override
    public void cancel() {

    }
}

/**
 * Flink中datasource的使用
 */
public class FlinkDataSourceApp {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        /**
         * 使用内置的dataSource
         */
//        DataStreamSource<String> source = env.readFile(new TextInputFormat(null), "data/wc.data");
//        // 这个readTextFile方法底层其实调用的就是readFile
//        DataStreamSource<String> source = env.readTextFile("data/wc.txt");
//        System.out.println(source.getParallelism());  // 8
//
//        SingleOutputStreamOperator<String> mapStream = source.map(String::toUpperCase);
//        System.out.println(mapStream.getParallelism());
//        mapStream.print();
//
//        DataStreamSource<Long> source = env.fromParallelCollection(new NumberSequenceIterator(1, 10), Long.TYPE);
//        System.out.println(source.getParallelism());// 8
//        SingleOutputStreamOperator<Long> map = source.map(x -> x + 1);
//        map.print();
//
//        DataStreamSource<Access> source = env.addSource(new AccessSourceV2()).setParallelism(3); // 对于ParallelSourceFunction是可以根据具体情况来设定并行度的
//        System.out.println(source.getParallelism());
//        source.print();

        /**
         * 使用自定义数据源
         */
//        env.addSource(new AccessSource()).print();
//        env.addSource(new AccessSourceV2()).setParallelism(3).print(); // 多并行度的可以自行设置并行度

        /**
         * 使用Flink自定义MySQL的数据源,进而读取MySQL里面的数据
         * 该方式已过时 …… flink更新太快了
         */
        env.addSource(new MySQLSource()).print();


        /**
         * 单并行度:fromElements  fromCollection  socketTextStream
         * 多并行度:readTextFile fromParallelCollection generateSequence  readFile
         * 自定义:
         */
        env.execute("作业名字");
    }
}

(3)使用Source接口

暂无

4、数据处理(Transformation)

(1)总览

官方文档:
https://nightlies.apache.org/flink/flink-docs-release-1.18/docs/dev/datastream/operators/overview/

运算符将一个或多个数据流转换为新的数据流。程序可以将多种转换组合成复杂的数据流拓扑。

(2)Map(后续用该结果测试)

把DataStream转换成新的DataStream。

// 将读取的文件,按照,分割,然后每一行数据组成一个Access对象
DataStream<Integer> dataStream = env.readTextFile("data/access.log");
SingleOutputStreamOperator<Access> mapStream = dataStream.map(new MapFunction<String, Access>() {
    @Override
    public Access map(String value) throws Exception {
        String[] splits = value.split(",");
        Access access = new Access();
        access.setTime(Long.parseLong(splits[0].trim()));
        access.setDomain(splits[1].trim());
        access.setTraffic(Double.parseDouble(splits[2].trim()));
        return access;
    }
});
mapStream.print();

(3)Filter

把DataStream转换成新的DataStream。
计算每个元素的布尔函数,并保留函数返回true的元素。
也即:过滤出满足条件的元素。

// 过滤出不为0的元素
dataStream.filter(new FilterFunction<Integer>() {
    @Override
    public boolean filter(Integer value) throws Exception {
        return value != 0; 
    }
});

(4)FlatMap

把DataStream转换成新的DataStream。
可以是一对一、一对多、一对0 一个元素进来,可以出去0、1、多个元素。

dataStream.flatMap(new FlatMapFunction<String, String>() {
    @Override
    public void flatMap(String value, Collector<String> out)
        throws Exception {
        for(String word: value.split(" ")){ // 把每一个元素按空格分割
            out.collect(word); // 收集每一个 分割后的 元素
        }
    }
});

(5)KeyBy

把DataStream转换为KeyedStream 。

在逻辑上将流划分为不相交的分区。具有相同关键字的所有记录都被分配到同一个分区
在内部,keyBy()是通过散列分区实现的。
(类似Map - Reduce思想)
注意!如果是根据一个对象分组,要重写 hashCode()方法,否则会使用默认的Object.hashCode()。

// 根据value的某个属性分组,相当于mysql的group by
// 通常分组之后,就要求和、求一些统计数据了
dataStream.keyBy(value -> value.getSomeKey());
dataStream.keyBy(value -> value.f0);

dataStream
.keyBy(value -> value.getSomeKey())
.sum("field") // 根据字段求和还可以求最大值最小值等
.print();

(6)Reduce

把KeyedStream 转换为 DataStream。
将当前元素与最后一个减少的值合并,并发出新值。

keyedStream.reduce(new ReduceFunction<Integer>() {
    @Override
    public Integer reduce(Integer value1, Integer value2)
    throws Exception {
        return value1 + value2;
    }
});

(7)Union

把多个DataStream合并为一个DataStream。
两个或多个数据流的联合,创建一个包含所有流中所有元素的新流。注意:如果您将数据流与其自身联合,您将在结果流中两次获得每个元素。

/**
 * union:合并多个流
 * 数据类型问题:union的多个流中数据类型是需要相同的
 * 数据类型相同的多流操作
 */
DataStreamSource<Integer> stream1 = env.fromElements(1, 2, 3);
DataStreamSource<Integer> stream2 = env.fromElements(11, 12, 13);
DataStreamSource<String> stream3 = env.fromElements("A", "B", "C");

stream1.union(stream2).map(x -> "PK_" + x).print();
stream1.union(stream1).print();
stream1.union(stream1, stream2).print();

(8)Connect

把两个DataStream 合并为 ConnectedStream。

DataStream<Integer> someStream = //...
DataStream<String> otherStream = //...
ConnectedStreams<Integer, String> connectedStreams = someStream.connect(otherStream);
/**
 * connect: 数据类型可以不同
 * 两个流的操作
 * 只是形式的连接
 */
ConnectedStreams<Integer, String> connectedStreams = stream1.connect(stream3);
connectedStreams.map(new CoMapFunction<Integer, String, String>() {
    // 共享状态
    String prefix = "common_";

    // 对第一个流的操作
    @Override
    public String map1(Integer value) throws Exception {
        return prefix + value*10;
    }

    // 对第二个流的操作
    @Override
    public String map2(String value) throws Exception {
        return prefix + value.toLowerCase();
    }
}).print();

(9)CoMap, CoFlatMap

将ConnectedStream 转换为 DataStream。
类似于连接数据流上的map和flatMap。

connectedStreams.map(new CoMapFunction<Integer, String, Boolean>() {
    @Override
    public Boolean map1(Integer value) {
        return true;
    }

    @Override
    public Boolean map2(String value) {
        return false;
    }
});
connectedStreams.flatMap(new CoFlatMapFunction<Integer, String, String>() {

   @Override
   public void flatMap1(Integer value, Collector<String> out) {
       out.collect(value.toString());
   }

   @Override
   public void flatMap2(String value, Collector<String> out) {
       for (String word: value.split(" ")) {
         out.collect(word);
       }
   }
});

(10)Physical Partitioning 分区

import org.apache.flink.api.common.functions.MapFunction;
import org.apache.flink.api.common.functions.Partitioner;
import org.apache.flink.api.common.functions.RichMapFunction;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

public class PartitionTest2 {

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        env.setParallelism(3); // 一般情况下,并行度跟分区相同,相同分区在同一个线程中执行
        DataStreamSource<String> sourcePartition = env.readTextFile("data/access.log");
        sourcePartition
                // 读取数据转成Access对象
                .map(new RichMapFunction<String, Access>() {
                    @Override
                    public Access map(String value) throws Exception {
                        String[] splits = value.split(",");
                        Access access = new Access();
                        access.setTime(Long.parseLong(splits[0].trim()));
                        access.setDomain(splits[1].trim());
                        access.setTraffic(Double.parseDouble(splits[2].trim()));
                        return access;
                    }
                })
                // 按照指定字段进行分区
                .partitionCustom(new Partitioner<String>() {
                    @Override
                    public int partition(String key, int numPartitions) {
                        System.out.println(numPartitions);

                        if("test1.com".equals(key)) {
                            return 0;
                        } else if("test2.com".equals(key)) {
                            return 1;
                        } else {
                            return 2;
                        }
                    }
                }, x -> x.getDomain())
                // 下面的这段map方法目的是验证:相同的域名是否真的在同一个分区内,看threadid是否相同即可
                .map(new MapFunction<Access, Access>() {
                    @Override
                    public Access map(Access value) throws Exception {
                        System.out.println("current thread id is " + Thread.currentThread().getId() + ", value is:" + value);
                        return value;
                    }
                }).print();
        env.execute("作业名字");
    }
}

(11)Side Outputs 分流操作

https://nightlies.apache.org/flink/flink-docs-release-1.18/docs/dev/datastream/side_output/

/**
 * 分流操作:把一个分拆分成多个流
 *
 * split 在老的flink版本中是有的,但是新的版本中已经没有这个api
 *
 * 那就说明新版本肯定提供了更好用的使用方式:side output
 */

DataStreamSource<String> source = env.readTextFile("data/access.log");
SingleOutputStreamOperator<Access> stream = source.map(new AccessConvertFunction());

// 很low的写法
//        SingleOutputStreamOperator<Access> pk1Stream = stream.filter(x -> "test1.com".equals(x.getDomain()));
//        SingleOutputStreamOperator<Access> pk2Stream = stream.filter(x -> "test1.com".equals(x.getDomain()));
//        pk1Stream.print("域名是pk1.com的流");
//        pk2Stream.print("域名是pk2.com的流");

// 定义两个Tag
OutputTag<Access> test1OutputTag = new OutputTag<Access>("test1"){};
OutputTag<Access> test2OutputTag = new OutputTag<Access>("test2"){};


SingleOutputStreamOperator<Access> processStream = stream.process(new ProcessFunction<Access, Access>() {
    @Override
    public void processElement(Access value, Context ctx, Collector<Access> out) throws Exception {
        if ("test1.com".equals(value.getDomain())) {
            ctx.output(test1OutputTag, value); // pk1.com的走pk1的OutputTag
        } else if ("test2.com".equals(value.getDomain())) {
            ctx.output(test2OutputTag, value);  // pk2.com的走pk2的OutputTag
        } else {
            out.collect(value); // pk3.com的走主流
        }
    }
});

processStream.print("主流:");
processStream.getSideOutput(test1OutputTag).print("test1的:");
processStream.getSideOutput(test2OutputTag).print("test2的:");

env.execute("作业名字");

5、数据输出(Data Sinks、Connectors)

(1)总览

https://nightlies.apache.org/flink/flink-docs-release-1.18/docs/dev/datastream/overview/#data-sinks
https://nightlies.apache.org/flink/flink-docs-release-1.18/docs/connectors/datastream/overview/

数据接收器消费数据流,并将它们转发到文件、Socket、外部系统或打印它们。
Flink自带多种内置输出格式:
writeAsText() / TextOutputFormat:将元素作为字符串逐行写入。字符串是通过调用每个元素的toString()方法获得的。
writeAsCsv(...) / CsvOutputFormat:将元组写入逗号分隔值文件。行和字段分隔符是可配置的。每个字段的值来自对象的toString()方法。
print() / printToErr():打印标准输出/标准错误流中每个元素的toString()值。可选地,可以提供一个前缀(msg),将其附加到输出的前面。这有助于区分不同的打印调用。如果并行度大于1,输出还将加上产生输出的任务的标识符。
writeUsingOutputFormat() / FileOutputFormat:自定义文件输出的方法和基类。支持自定义对象到字节的转换。
writeToSocket:根据SerializationSchema将元素写入Socket。
addSink:调用自定义接收器函数。Flink与其他系统(如Apache Kafka)的连接器捆绑在一起,这些连接器被实现为sink函数。

(2)print

stream.print();
/*
>7> Access{time=202810110120, domain='test1.com', traffic=2000.0}
1> Access{time=202810110120, domain='test2.com', traffic=4000.0}
11> Access{time=202810110120, domain='test1.com', traffic=5000.0}
4> Access{time=202810110120, domain='test3.com', traffic=1000.0}
9> Access{time=202810110120, domain='test2.com', traffic=6000.0}
线程号 + 数据.toString()

如果这样:
stream.print().setParallelism(1);
并行度设置为1,那么前面就不会输出数字

这样打印红色:
stream.printToErr();
*/

源码:
在这里插入图片描述

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

(3)自定义Sink打印到控制台

stream.addSink(new RichSinkFunction<Access>() {

    int subTaskId;

    // num>
    @Override
    public void open(Configuration parameters) throws Exception {
        super.open(parameters);

        subTaskId = getRuntimeContext().getIndexOfThisSubtask();
    }

    @Override
    public void invoke(Access value, SinkFunction.Context context) throws Exception {
        System.out.println(subTaskId + 1 + "> " + value); // 最终执行的方法,输出到终端
    }
});

(4)写入到文件

// 已过时
// 写入到文件,每一个并行度,会生成一个文件。并行度为1会生成test一个文件
stream.writeAsText("out/test", FileSystem.WriteMode.OVERWRITE).setParallelism(1);

// 也已经过时了,推荐使用 org.apache.flink.connector.file.sink.FileSink,需要额外引入包
StreamingFileSink<String> fileSink = StreamingFileSink
        .forRowFormat(new Path("out"), new SimpleStringEncoder())
        .withRollingPolicy(DefaultRollingPolicy.builder() // 构建文本滚动生成的策略
                .withRolloverInterval(Duration.ofMinutes(15)) // 按时间间隔滚动
                .withInactivityInterval(Duration.ofSeconds(5)) // 按不活跃滚动
                .withMaxPartSize(MemorySize.ofMebiBytes(1)) // 按大小滚动
                .build())
        .build();
// 数据类型需要前后对应
stream.map(Access::toString).addSink(fileSink);

(5)输出到MySQL

JdbcSink.sink提供至少一次保证。然而有效的是,通过创建upsert SQL语句或幂等SQL更新可以实现“恰好一次”。

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-connector-jdbc</artifactId>
    <version>3.1.1-1.17</version>
</dependency>
// 写入到mysql
// 需要使用upsert语句
SinkFunction<Access> jdbcSink = JdbcSink.sink(
        // sql
        "insert into access (id, name) values (?, ?) on duplicate key update name=VALUES(name)",
        // sql的参数
        (JdbcStatementBuilder<Access>) (preparedStatement, access) -> {
            preparedStatement.setInt(1, (int)access.getTraffic());
            preparedStatement.setString(2, access.getDomain());
        },
        // 执行参数
        JdbcExecutionOptions.builder()
                .withBatchSize(5)
                .withBatchIntervalMs(200)
                .withMaxRetries(5) // 重试
                .build(),
        // jdbc连接信息
        new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
                .withUrl("jdbc:mysql://192.168.56.10:3306/testdb")
                .withDriverName("com.mysql.jdbc.Driver")
                .withUsername("root")
                .withPassword("root")
                .build()
);

stream.addSink(jdbcSink);

自1.13起, Flink JDBC sink支持恰好一次模式。该实现依赖于XA标准的JDBC驱动程序支持。如果数据库也支持XA,则大多数驱动程序都支持XA(因此驱动程序通常是相同的)。

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
env
        .fromElements(...)
        .addSink(JdbcSink.exactlyOnceSink(
                "insert into books (id, title, author, price, qty) values (?,?,?,?,?)",
                (ps, t) -> {
                    ps.setInt(1, t.id);
                    ps.setString(2, t.title);
                    ps.setString(3, t.author);
                    ps.setDouble(4, t.price);
                    ps.setInt(5, t.qty);
                },
                JdbcExecutionOptions.builder()
                    .withMaxRetries(0)
                    .build(),
                JdbcExactlyOnceOptions.defaults(),
                () -> {
                    // create a driver-specific XA DataSource
                    // The following example is for derby 
                    EmbeddedXADataSource ds = new EmbeddedXADataSource();
                    ds.setDatabaseName("my_db");
                    return ds;
                });
env.execute();

(6)输出到Redis

https://bahir.apache.org/docs/flink/current/flink-streaming-redis/

public static class RedisExampleMapper implements RedisMapper<Tuple2<String, String>>{

    @Override
    public RedisCommandDescription getCommandDescription() {
        return new RedisCommandDescription(RedisCommand.HSET, "HASH_NAME");
    }

    @Override
    public String getKeyFromData(Tuple2<String, String> data) {
        return data.f0;
    }

    @Override
    public String getValueFromData(Tuple2<String, String> data) {
        return data.f1;
    }
}
FlinkJedisPoolConfig conf = new FlinkJedisPoolConfig.Builder().setHost("127.0.0.1").build();

DataStream<String> stream = ...;
stream.addSink(new RedisSink<Tuple2<String, String>>(conf, new RedisExampleMapper());

(7)输出到Socket

// 输出到Socket,注意类型匹配,输出为字符串
stream.map(Access::toString).writeToSocket("localhost", 9528, new SimpleStringSchema());

(8)输出到Kafka

https://nightlies.apache.org/flink/flink-docs-release-1.18/docs/connectors/datastream/kafka/

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

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

相关文章

服务器与电脑的区别?

目录 一、什么是服务器 二、什么是电脑 三、服务器和电脑的区别 一、什么是服务器 服务器是指一种专门提供计算和存储资源、运行特定软件服务的物理或虚拟计算机。服务器主要用于接受和处理来自客户端&#xff08;如个人电脑、手机等&#xff09;的请求&#xff0c;并向客户…

制作耳机壳的UV树脂和塑料材质相比优势有哪些?

制作耳机壳的UV树脂相比塑料材质有以下优势&#xff1a; 高强度与耐磨性&#xff1a;UV树脂具有高强度和耐磨性&#xff0c;能够更好地保护耳机内部零件&#xff0c;延长耳机使用寿命。相比之下&#xff0c;塑料材质可能较易磨损或刮伤。耐高温&#xff1a;UV树脂具有较好的耐…

【Web】vulhub Shiro-550反序列化漏洞复现学习笔记

目录 Shiro简介 复现流程 工具一把梭 半脚本半手动 原理分析 反序列化入口 常见的key 登录过程 验证过程 利用原理 Shiro简介 Apache Shiro 是一个强大且易于使用的 Java 安全框架&#xff0c;用于身份验证、授权、加密和会话管理等安全功能。Shiro 的设计目标是简单…

什么是向量数据库?为什么向量数据库对LLM很重要?

由于我们目前生活在人工智能革命之中&#xff0c;重要的是要了解许多新应用程序都依赖于向量嵌入&#xff08;vector embedding&#xff09;。因此&#xff0c;有必要了解向量数据库以及它们对 LLM 的重要性。 我们首先定义向量嵌入。向量嵌入是一种携带语义信息的数据表示形式…

springboot172基于springboot的二手车交易系统的设计与实现

二手车交易系统的设计与实现 摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统二手车交易信息管理难度大&…

armbian ddns

参考https://mp.weixin.qq.com/s/0Uu_nbGH_W6vAYHPH4kHqg Releases jeessy2/ddns-go GitHub mkdir -p /usr/local/ddns-go cd /usr/local/ddns-gowget https://github.com/jeessy2/ddns-go/releases/download/v6.1.1/ddns-go_6.1.1_freebsd_armv7.tar.gztar zxvf ddns-go_…

【XR806开发板试用】轻松连上华为云实现物联网

本文为极术社区XR806试用活动文章。 一.开始 偶然的机会在网上看到了鸿蒙开发板的试用,作为一个"老鸿蒙"岂能放弃这个机会,报名之后不出意料地得到了使用名额,在此感谢极术社区. 收到开发板之后其实还有点失望了,就那么一个小小的核心板,其他啥也没有,连一根数据线…

【PyQt】08 - 编辑Tab顺序

文章目录 前言一、Tab顺序二、编辑Tab顺序总结 前言 介绍了什么是Tab顺序&#xff0c;以及如何修改Tab顺序。 一、Tab顺序 当你的界面设计好之后&#xff0c;在输入栏按住Tab按键&#xff0c;他会按照你摆放的顺序一次转跳 二、编辑Tab顺序 方法一 然后鼠标左击就可以改变…

CTFshow web(php命令执行 45-49)

基础知识&#xff1a; 1.绕过cat使用&#xff1a; tac more less head tac tail nl od(二进制查看) vi vim sort uniq rev 2.绕过空格用&#xff1a; %09 <> ${IFS} $IFS$ {cat,fl*} %20 注&#xff1a; %09 ##&#xff08;Tab&#xff09; %20 ##&#xff08;spa…

Swift Combine 发布者publisher的生命周期 从入门到精通四

Combine 系列 Swift Combine 从入门到精通一Swift Combine 发布者订阅者操作者 从入门到精通二Swift Combine 管道 从入门到精通三 1. 发布者和订阅者的生命周期 订阅者和发布者以明确定义的顺序进行通信&#xff0c;因此使得它们具有从开始到结束的生命周期&#xff1a; …

MATLAB环境下基于深层小波时间散射网络的ECG信号分类

2012年&#xff0c;法国工程学院院士Mallat教授深受深度学习结构框架思想的启发&#xff0c;提出了基于小波变换的小波时间散射网络&#xff0c;并以此构造了小波时间散射网络。 小波时间散射网络的结构类似于深度卷积神经网络&#xff0c;不同的是其滤波器是预先确定好的小波…

Postman(接口测试工具),什么是Postman接口

目录 一.基本介绍 Postman 是什么Postman 快速入门快速入门需求说明 二.Postman 完成 Controller 层测试 需要的代码&#xff1a; Java类request.jspsuccess.jsp1. 完成请求2. 完成请求3. 完成请求4. 完成请求5. 完成请求 三.发送join 目录 一.基本介绍 Postman 是什么 …

Redis核心技术与实战【学习笔记】 - 31.番外篇:Redis客户端如何与服务器端交换命令和数据

简述 Redis 使用 RESP 协议&#xff08;Redis Serialzation Protocol&#xff09;协议定义了客户端和服务器端交互的命令、数据的编码格式。在 Redis 2.0 版本中&#xff0c;RESP 协议正式称为客户端和服务器端的标准通信协议。从 Redis 2.0 到 Redis 5.0 &#xff0c;RESP 协…

电脑通电自启动设置

首先要进入BIOS&#xff0c;以华硕为例&#xff0c;按下电源键&#xff0c;在开机之前按下delete键&#xff0c;其他电脑可能是esc或者某个f键&#xff0c;请自行查找。 进入BIOS后要找到电源管理&#xff0c;可以在高级选项中找一找&#xff0c;如上图右下角选择高级模式。 …

机器学习10-特征缩放

特征缩放的目的是确保不同特征的数值范围相近&#xff0c;使得模型在训练过程中更加稳定&#xff0c;加速模型收敛&#xff0c;提高模型性能。具体而言&#xff0c;零均值和单位方差的目标有以下几点好处&#xff1a; 1. 均值为零&#xff08;Zero Mean&#xff09;&#xff1a…

移动应用开发Android 创建第一个Android项目

文章目录 一、创建第一个Android项目1.1 准备好Android Studio1.2 运行程序1.3 程序结构是什么app下的结构res - 子目录&#xff08;所有图片、布局、字AndroidManifest.xml 有四大组件&#xff0c;程序添加权限声明 Project下的结构 二、开发android时&#xff0c;部分库下载异…

教师题不会怎么搜答案?用这三款神器就够了!!! #笔记#知识分享#职场发展

在大学生的学习过程中&#xff0c;遇到难题和疑惑是常有的事情。然而&#xff0c;随着互联网的普及和技术的发展&#xff0c;搜题和学习软件成为了大学生们解决问题的利器。今天&#xff0c;我将向大家推荐几款备受大学生喜爱的搜题和学习软件&#xff0c;帮助我们更好地应对学…

汽车零部件MES系统解决方案

汽车零部件行业现状 随着全球汽车产业不断升级&#xff0c;汽车零部件市场竞争日趋激烈&#xff0c;从上游的钢铁、塑料、橡胶等生产到下游的主机厂配套制造&#xff0c;均已成为全球各国汽车制造大佬战略目标调整的焦点&#xff0c;其意欲在汽车零部件行业快速开疆扩土&#x…

HARRYPOTTER: FAWKES

攻击机 192.168.223.128 目标机192.168.223.143 主机发现 nmap -sP 192.168.223.0/24 端口扫描 nmap -sV -p- -A 192.168.223.143 开启了21 22 80 2222 9898 五个端口&#xff0c;其中21端口可以匿名FTP登录&#xff0c;好像有点说法,百度搜索一下发现可以用anonymous登录…

NLP_语言模型的雏形 N-Gram 模型

文章目录 N-Gram 模型1.将给定的文本分割成连续的N个词的组合(N-Gram)2.统计每个N-Gram在文本中出现的次数&#xff0c;也就是词频3.为了得到一个词在给定上下文中出现的概率&#xff0c;我们可以利用条件概率公式计算。具体来讲&#xff0c;就是计算给定前N-1个词时&#xff0…