Flink Sink的使用

news2024/11/28 22:32:13

经过一系列Transformation转换操作后,最后一定要调用Sink操作,才会形成一个完整的DataFlow拓扑。只有调用了Sink操作,才会产生最终的计算结果,这些数据可以写入到的文件、输出到指定的网络端口、消息中间件、外部的文件系统或者是打印到控制台.

flink在批处理中常见的sink

  1. print 打印
  2. writerAsText 以文本格式输出
  3. writeAsCsv 以csv格式输出
  4. writeUsingOutputFormat 以指定的格式输出
  5. writeToSocket 输出到网络端口
  6. 自定义连接器(addSink)

参考官网:https://ci.apache.org/projects/flink/flink-docs-release-1.13/zh/docs/dev/datastream/overview/#data-sinks

1、print

打印是最简单的一个Sink,通常是用来做实验和测试时使用。如果想让一个DataStream输出打印的结果,直接可以在该DataStream调用print方法。另外,该方法还有一个重载的方法,可以传入一个字符,指定一个Sink的标识名称,如果有多个打印的Sink,用来区分到底是哪一个Sink的输出。

以下演示了print打印,以及自定义print打印。

package com.bigdata.day03;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;


public class SinkPrintDemo {

    public static void main(String[] args) throws Exception {
        //1. env-准备环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        DataStreamSource<String> dataStreamSource = env.socketTextStream("localhost", 8888);
        // 打印,普通的打印
        // 6> helllo world
        //dataStreamSource.print();

        dataStreamSource.addSink(new MySink());

        // 接着手动实现该print 打印

        env.execute();
    }

    static class MySink extends RichSinkFunction<String> {

        @Override
        public void invoke(String value, Context context) throws Exception {
            // 得到一个分区号,因为要模仿print打印效果
            int partitionId = getRuntimeContext().getIndexOfThisSubtask() + 1;
            String msg = partitionId +"> " +value;
            System.out.println(msg);
        }
    }

}

 

package com.bigdata.day03;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;


public class Demo01 {
    static class MyPrint extends RichSinkFunction<String>{
        private String msg;
        public MyPrint(){

        }
        public MyPrint(String msg){
            this.msg = msg;
        }
        @Override
        public void invoke(String value, Context context) throws Exception {
            int partition = getRuntimeContext().getIndexOfThisSubtask();
            if(msg == null){
                System.out.println(partition+"> "+value);
            }else{
                System.out.println(msg+">>>:"+partition+"> "+value);
            }

        }
    }

    public static void main(String[] args) throws Exception {

        //1. env-准备环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        //2. source-加载数据
        DataStream<String> data = env.fromElements("hello", "world", "baotianman", "laoyan");
        //3. transformation-数据处理转换
        //4. sink-数据输出
        //data.print();
        //data.print("普通打印>>>");
        data.addSink(new MyPrint());
        data.addSink(new MyPrint("模仿:"));


        //5. execute-执行
        env.execute();
    }
}

 

下面的结果是WordCount例子中调用print Sink输出在控制台的结果,细心的读者会发现,在输出的单词和次数之前,有一个数字前缀,我这里是1~4,这个数字是该Sink所在subtask的Index + 1。有的读者运行的结果数字前缀是1~8,该数字前缀其实是与任务的并行度相关的,由于该任务是以local模式运行,默认的并行度是所在机器可用的逻辑核数即线程数,我的电脑是2核4线程的,所以subtask的Index范围是0~3,将Index + 1,显示的数字前缀就是1~4了。这里在来仔细的观察一下运行的结果发现:相同的单词输出结果的数字前缀一定相同,即经过keyBy之后,相同的单词会被shuffle到同一个subtask中,并且在同一个subtask的同一个组内进行聚合。一个subtask中是可能有零到多个组的,如果是有多个组,每一个组是相互独立的,累加的结果不会相互干扰。

sum之后的:

1> hello 3

2> world 4

汇总之前,keyBy之后

1> hello 1

1> hello 1

1> hello 1

2、writerAsText 以文本格式输出

该方法是将数据以文本格式实时的写入到指定的目录中,本质上使用的是TextOutputFormat格式写入的。每输出一个元素,在该内容后面同时追加一个换行符,最终以字符的形式写入到文件中,目录中的文件名称是该Sink所在subtask的Index + 1。该方法还有一个重载的方法,可以额外指定一个枚举类型的参数writeMode,默认是WriteMode.NO_OVERWRITE,如果指定相同输出目录下有相同的名称文件存在,就会出现异常。如果是WriteMode.OVERWRITE,会将以前的文件覆盖。

package com.bigdata.day03;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.core.fs.FileSystem;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;


public class SinkTextDemo {

    public static void main(String[] args) throws Exception {
        //1. env-准备环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);
        env.setParallelism(2);

        DataStreamSource<String> dataStreamSource = env.socketTextStream("localhost", 8880);
        // 写入到文件的时候,OVERWRITE 模式是重写的意思,假如以前有结果直接覆盖
        // 如果并行度为1 ,最后输出的结果是一个文件,假如并行度 > 1 最后的结果是一个文件夹,文件夹中的文件名是 分区号(任务号)

        dataStreamSource.writeAsText("F:\\BD230801\\FlinkDemo\\datas\\result", FileSystem.WriteMode.OVERWRITE);

        env.execute();
    }
}
package com.bigdata.day03;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.core.fs.FileSystem;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;


public class Demo02 {

    public static void main(String[] args) throws Exception {

        //1. env-准备环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        //2. source-加载数据
        //3. transformation-数据处理转换
        //4. sink-数据输出
        //DataStreamSource<String> streamSource = env.socketTextStream("localhost", 8899);
        //streamSource.writeAsText("datas/socket", FileSystem.WriteMode.OVERWRITE).setParallelism(1);
        DataStreamSource<Tuple2<String, Integer>> streamSource = env.fromElements(
                Tuple2.of("篮球", 1),
                Tuple2.of("篮球", 2),
                Tuple2.of("篮球", 3),
                Tuple2.of("足球", 3),
                Tuple2.of("足球", 2),
                Tuple2.of("足球", 3)
        );
        // writeAsCsv 只能保存 tuple类型的DataStream流,因为如果不是多列的话,没必要使用什么分隔符
        streamSource.writeAsCsv("datas/csv", FileSystem.WriteMode.OVERWRITE).setParallelism(1);


        //5. execute-执行
        env.execute();
    }
}

 3、连接器Connectors

JDBC Connector

该连接器可以向JDBC 数据库写入数据

JDBC | Apache Flink

<dependency>
  <groupId>org.apache.flink</groupId>
  <artifactId>flink-connector-jdbc_2.11</artifactId>
  <version>${flink.version}</version>
</dependency>

<!--假如你是连接低版本的,使用5.1.49-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.25</version>
        </dependency>

案例演示:

将结果读取,写入到MySQL

package com.bigdata.day03;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.flink.connector.jdbc.JdbcConnectionOptions;
import org.apache.flink.connector.jdbc.JdbcExecutionOptions;
import org.apache.flink.connector.jdbc.JdbcSink;
import org.apache.flink.connector.jdbc.JdbcStatementBuilder;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

import java.sql.PreparedStatement;
import java.sql.SQLException;

@Data
@AllArgsConstructor
@NoArgsConstructor
class Student{
    private int id;
    private String name;
    private int age;
}
public class JdbcSinkDemo {


    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        DataStreamSource<Student> studentStream = env.fromElements(new Student(1, "jack", 54));


        JdbcConnectionOptions jdbcConnectionOptions = new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
                .withUrl("jdbc:mysql://localhost:3306/test1")
                .withDriverName("com.mysql.jdbc.Driver")
                .withUsername("root")
                .withPassword("123456")
                .build();
        studentStream.addSink(JdbcSink.sink(
                "insert into student values(null,?,?)",
                new JdbcStatementBuilder<Student>() {
                    @Override
                    public void accept(PreparedStatement preparedStatement, Student student) throws SQLException {

                        preparedStatement.setString(1,student.getName());
                        preparedStatement.setInt(2,student.getAge());
                    }
                    // 假如是流的方式可以设置两条插入一次
                }, JdbcExecutionOptions.builder().withBatchSize(2).build(),jdbcConnectionOptions

        ));

        env.execute();
    }
}
package com.bigdata.day03;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.connector.jdbc.JdbcConnectionOptions;
import org.apache.flink.connector.jdbc.JdbcExecutionOptions;
import org.apache.flink.connector.jdbc.JdbcSink;
import org.apache.flink.connector.jdbc.JdbcStatementBuilder;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;

import java.sql.PreparedStatement;
import java.sql.SQLException;


@Data
@AllArgsConstructor
@NoArgsConstructor
class Student{
    private int id;
    private String name;
    private int age;
}
public class Demo03 {

    public static void main(String[] args) throws Exception {

        //1. env-准备环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);
        DataStreamSource<Student> studentDataStreamSource = env.fromElements(
                new Student(1, "张三", 19),
                new Student(2, "lisi", 20),
                new Student(3, "wangwu", 19)
        );

        JdbcConnectionOptions jdbcConnectionOptions = new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
                .withUrl("jdbc:mysql://localhost:3306/kettle")
                .withDriverName("com.mysql.cj.jdbc.Driver")
                .withUsername("root")
                .withPassword("root")
                .build();

        studentDataStreamSource.addSink(JdbcSink.sink(
                "insert into student values(null,?,?)",
                new JdbcStatementBuilder<Student>() {
                    @Override
                    public void accept(PreparedStatement preparedStatement, Student student) throws SQLException {
                        preparedStatement.setString(1,student.getName());
                        preparedStatement.setInt(2,student.getAge());
                    }
                },jdbcConnectionOptions
        ));
        //2. source-加载数据
        //3. transformation-数据处理转换
        //4. sink-数据输出


        //5. execute-执行
        env.execute();
    }
}

运行结果正常:

KafkaConnector

Kafka | Apache Flink

从Kafka的topic1中消费日志数据,并做实时ETL,将状态为success的数据写入到Kafka的topic2中

 

kafka-topics.sh --bootstrap-server bigdata01:9092 --create --partitions 1 --replication-factor 3 --topic topic1
kafka-topics.sh --bootstrap-server bigdata01:9092 --create --partitions 1 --replication-factor 3 --topic topic2

使用控制台当做kafka消息的生产者向kafka中的topic1 发送消息
kafka-console-producer.sh  --bootstrap-server bigdata01:9092 --topic topic1

消费kafka中topic2中的数据
kafka-console-consumer.sh  --bootstrap-server bigdata01:9092 --topic topic2

操作:
通过黑窗口向topic1中发送消息,含有success字样的消息,会出现在topic2中。

package com.bigdata.day03;

import org.apache.flink.api.common.RuntimeExecutionMode;
import org.apache.flink.api.common.functions.FilterFunction;
import org.apache.flink.api.common.serialization.SimpleStringSchema;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaConsumer;
import org.apache.flink.streaming.connectors.kafka.FlinkKafkaProducer;
import org.apache.kafka.clients.producer.KafkaProducer;

import java.util.Properties;


public class KafkaSinkDemo {

    // 从topic1中获取数据,放入到topic2中,训练了读和写
    public static void main(String[] args) throws Exception {

        //1. env-准备环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.setRuntimeMode(RuntimeExecutionMode.AUTOMATIC);

        //2. source-加载数据
        Properties properties = new Properties();
        properties.setProperty("bootstrap.servers", "bigdata01:9092");
        properties.setProperty("group.id", "g1");
        FlinkKafkaConsumer<String> kafkaSource = new FlinkKafkaConsumer<String>("topic1",new SimpleStringSchema(),properties);
        DataStreamSource<String> dataStreamSource = env.addSource(kafkaSource);
        //3. transformation-数据处理转换
        SingleOutputStreamOperator<String> filterStream = dataStreamSource.filter(new FilterFunction<String>() {
            @Override
            public boolean filter(String s) throws Exception {
                return s.contains("success");
            }
        });
        //4. sink-数据输出
        FlinkKafkaProducer kafkaProducer = new FlinkKafkaProducer<String>("topic2",new SimpleStringSchema(),properties);
        filterStream.addSink(kafkaProducer);


        //5. execute-执行
        env.execute();
    }
}

Flink Kafka Consumer 需要知道如何将 Kafka 中的二进制数据转换为 Java 或者Scala 对象。KafkaDeserializationSchema 允许用户指定这样的 schema,每条 Kafka 中的消息会调用 T deserialize(ConsumerRecord<byte[], byte[]> record) 反序列化。

为了方便使用,Flink 提供了以下几种 schemas:

SimpleStringSchema:按照字符串方式序列化、反序列化

剩余还有 TypeInformationSerializationSchema、JsonDeserializationSchema、AvroDeserializationSchema等。

自定义Sink--模拟jdbcSink的实现

jdbcSink官方已经提供过了,此处仅仅是模拟它的实现,从而学习如何自定义sink

package com.bigdata.day03;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.flink.configuration.Configuration;
import org.apache.flink.connector.jdbc.JdbcConnectionOptions;
import org.apache.flink.connector.jdbc.JdbcExecutionOptions;
import org.apache.flink.connector.jdbc.JdbcSink;
import org.apache.flink.connector.jdbc.JdbcStatementBuilder;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.functions.sink.RichSinkFunction;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;



public class CustomJdbcSinkDemo {

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Student{
        private int id;
        private String name;
        private int age;
    }

    static class MyJdbcSink  extends RichSinkFunction<Student> {

        Connection conn =null;
        PreparedStatement ps = null;
        @Override
        public void open(Configuration parameters) throws Exception {
            // 这个里面编写连接数据库的代码
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test1", "root", "123456");
            ps = conn.prepareStatement("INSERT INTO `student` (`id`, `name`, `age`) VALUES (null, ?, ?)");
        }

        @Override
        public void close() throws Exception {
            // 关闭数据库的代码
            ps.close();
            conn.close();
        }

        @Override
        public void invoke(Student student, Context context) throws Exception {
            // 将数据插入到数据库中
            ps.setString(1,student.getName());
            ps.setInt(2,student.getAge());
            ps.execute();

        }
    }

    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        DataStreamSource<Student> studentStream = env.fromElements(new Student(1, "马斯克", 51));

        studentStream.addSink(new MyJdbcSink());

        env.execute();
    }
}

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

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

相关文章

【Spring MVC】如何获取cookie/session以及响应@RestController的理解,Header的设置

前言 &#x1f31f;&#x1f31f;本期讲解关于SpringMVC的编程之参数传递~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &#x1f386;那么废…

使用 exe4j 将 Spring Boot 项目打包为 EXE 可执行文件

使用 exe4j 将 Spring Boot 项目打包为 EXE 可执行文件 文章目录 使用 exe4j 将 Spring Boot 项目打包为 EXE 可执行文件什么是 exe4j准备工作打包 Spring Boot 项目为 EXE 文件1.启动 exe4j2. 选择项目类型3. 配置项目名称和输出目录4. 配置项目类型或可执行文件名称5. java配…

前端JavaScript(一)---基本介绍

Javascript是一种由Netscape(网景)的LiveScript发展而来的原型化继承的面向对象的动态类型的区分大小写的客户端脚本语言&#xff0c;主要目的是为了解决服务器端语言&#xff0c;比如Perl&#xff0c;遗留的速度问题&#xff0c;为客户提供更流畅的浏览效果。当时服务端需要对…

阿里Qwen系列开源模型介绍

模型种类丰富 Qwen2&#xff1a;包含Qwen2-0.5b、Qwen2-1.5b、Qwen2-7b、Qwen2-57b-a14b以及Qwen2-72b等五种规模的预训练和指令微调模型&#xff0c;其在多语言处理、长文本处理、代码生成、数学和逻辑推理等能力上&#xff0c;在mmlu、gpqa、humaneval等国际测评中得到了验证…

基于Java的小程序电商商城开源设计源码

近年来电商模式的发展越来越成熟&#xff0c;基于 Java 开发的小程序电商商城开源源码&#xff0c;为众多开发者和企业提供了构建个性化电商平台的有力工具。 基于Java的电子商城购物平台小程序的设计在手机上运行&#xff0c;可以实现管理员&#xff1b;首页、个人中心、用户…

开源 AI 智能名片 2 + 1 链动模式 S2B2C 商城小程序源码助力品牌共建:价值、策略与实践

摘要&#xff1a;在当今数字化商业环境下&#xff0c;品牌构建已演变为企业与消费者深度共建的过程。本文聚焦于“开源 AI 智能名片 2 1 链动模式 S2B2C 商城小程序源码”&#xff0c;探讨其如何融入品牌建设&#xff0c;通过剖析品牌价值构成&#xff0c;阐述该技术工具在助力…

力扣797. 所有可能的路径

算法思想 深度优先搜索&#xff08;DFS&#xff09;&#xff1a; 使用递归从节点 0 开始&#xff0c;探索所有从当前节点到终点 n−1 的路径。每次访问一个节点时&#xff0c;将该节点加入当前路径 path。 回溯法&#xff1a; 在递归返回时&#xff0c;通过 path.pop_back()…

AMD(Xilinx) FPGA配置Flash大小选择

目录 1 FPGA配置Flash大小的决定因素2 为什么选择的Flash容量大小为最小保证能够完成整个FPGA的配置呢&#xff1f; 1 FPGA配置Flash大小的决定因素 在进行FPGA硬件设计时&#xff0c;选择合适的配置Flash是我们进行硬件设计必须考虑的&#xff0c;那么配置Flash大小的选择由什…

Git简单介绍

一、 Git介绍与安装 1.1 Git简介 Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从很小到非常大的项目版本管理。 1.2集中式(SVN&#xff09; VS 分布式(git) 集中式版本控制系统&#xff0c;版本库是集中存放在中央服务器的&#xff0c;工作时要先从中央…

FreeSWITCH 简单图形化界面34 - 网络环境安全的情况下,进行任意SIP注册

FreeSWITCH 简单图形化界面34 -网络环境安全的情况下&#xff0c;进行任意SIP注册 测试环境1、前言2、参数3、实践一下 测试环境 http://myfs.f3322.net:8020/ 用户名&#xff1a;admin&#xff0c;密码&#xff1a;admin FreeSWITCH界面安装参考&#xff1a;https://blog.cs…

力扣 二叉树的层序遍历-102

二叉树的层序遍历-102 class Solution { public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> res; // 二维数组用来存储每层节点if (root nullptr)return res;queue<TreeNode*> q; // 队列用来进行层序遍历q.push(r…

鸿蒙学习使用本地真机运行应用/元服务 (开发篇)

文章目录 1、前提条件2、使用USB连接方式3、使用无线调试连接方式4、运行 1、前提条件 在Phone和Tablet中运行HarmonyOS应用/元服务的操作方法一致&#xff0c;可以采用USB连接方式或者无线调试的连接方式。两种连接方式是互斥的&#xff0c;只能使用一种&#xff0c;无法同时…

数据库导论

data 数据是数据库中存储的基本数据&#xff0c;描述事物的符号称为数据。 DB 数据库是长期存储在计算机内&#xff0c;有组织&#xff0c;可共享的大量数据的集合。数据库中的数据按照一定的数据模型组织&#xff0c;描述和存储&#xff0c;具有较小的冗余度&#xff0c;较…

数据结构 ——— 归并排序算法的实现

目录 归并排序的思想 归并排序算法的实现 归并排序的思想 将已经有序的子序列合并&#xff0c;得到完全有序的序列&#xff0c;即先使每个子序列有序后&#xff0c;再使子序列段间有序 若将两个有序表合并成一个有序表&#xff0c;称为二路归并 归并排序步骤示意图&#x…

【数据结构】【线性表】一文讲完队列(附C语言源码)

队列 队列的基本概念基本术语基本操作 队列的顺序实现顺序队列结构体的创建顺序队列的初始化顺序队列入队顺序队列出队顺序队列存在的问题分析循环队列代码汇总 队列的链式实现链式队列的创建链式队列初始化-不带头结点链式队列入队-不带头节点链式队列出队-不带头结点带头结点…

chrome允许http网站打开摄像头和麦克风

第一步 chrome://flags/#unsafely-treat-insecure-origin-as-secure 第二步 填入网址&#xff0c;点击启用 第三步 重启 Chrome&#xff1a;设置完成后&#xff0c;点击页面底部的 “Relaunch” 按钮&#xff0c;重新启动 Chrome 浏览器&#xff0c;使更改生效。

Spring Boot教程之十: 使用 Spring Boot 实现从数据库动态下拉列表

使用 Spring Boot 实现从数据库动态下拉列表 动态下拉列表&#xff08;或依赖下拉列表&#xff09;的概念令人兴奋&#xff0c;但编写起来却颇具挑战性。动态下拉列表意味着一个下拉列表中的值依赖于前一个下拉列表中选择的值。一个简单的例子是三个下拉框&#xff0c;分别显示…

DRM(数字权限管理技术)防截屏录屏----视频转hls流加密、web解密播放

提示&#xff1a;视频转hls流加密、web解密播放 需求&#xff1a;研究视频截屏时&#xff0c;播放器变黑&#xff0c;所以先研究的视频转hls流加密 文章目录 [TOC](文章目录) 前言一、工具ffmpeg、openssl二、后端nodeexpress三、web播放四、文档总结 前言 ‌HLS流媒体协议‌&a…

视觉经典神经网络与复现:深入解析与实践指南

目录 引言 经典视觉神经网络模型详解 1. LeNet-5&#xff1a;卷积神经网络的先驱 LeNet-5的关键特点&#xff1a; 2. AlexNet&#xff1a;深度学习的突破 AlexNet的关键特点&#xff1a; 3. VGGNet&#xff1a;深度与简洁的平衡 VGGNet的关键特点&#xff1a; 4. ResNe…

【算法day1】数组:双指针算法

题目引用 这里以 1、LeetCode704.二分查找 2、LeetCode27.移除元素 3、LeetCode977.有序数组的平方 这三道题举例来说明数组中双指针的妙用。 1、二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜…