1-Flume中agent的source

Flume(1.11.0版本)

简介

概述

  1. Flume本身是由Cloudera公司开发的后来贡献给了Apache的一套针对日志数据进行收集(collecting)、汇聚(aggregating)和传输(moving)的机制

  2. Flume本身提供了简单且灵活的结构来完成日志数据的传输

    Flume结构

  3. Flume有两大版本:

    1. Flume0.X:又称之为Flume-og,依赖于Zookeeper部署,需要提供的格式文件相对复杂,所以现在市面上已经不使用了
    2. Flume1.X:又称之为Flume-ng,不依赖于Zookeeper部署,需要提供的格式文件结构明确且简单,所以是现在流通的版本

基本概念

  1. Event

    1. Flume会将收集到的每一条日志封装成一个Event

    2. Event本质上就是一个json串,即Flume会将收集到的日志封装成json的形式,Event中固定的包含两部分:headers和body

      {"headers":{},"body":""}
      
  2. Agent:Flume流动模型的基本组成结构,至少包含3部分

    1. Source:从数据源采集数据 - collecting
    2. Channel:临时存储数据 - aggregating
    3. Sink:将数据写出到目的地 - moving

流动模型

  1. 单级流动

    Flume结构

  2. 多级流动:又称之为串联流动

    多级流动

  3. 扇入流动:又称之为并联流动、聚集流动

    扇入流动

  4. 扇出流动:又称之为复用流动

    扇出流动

  5. 复杂流动:按照需求将多个流动进行组合,那么就是复杂流动

参数解释

参数解释
--name或者-n指定要运行的agent的名字
--conf或者-cFlume的原生配置
--conf-file或者-f执行的文件
-D指定运行其他的参数
flume.root.logger指定日志的打印级别,级别分为INFOWARNERROR,可以指定打印位置consolelogfile

Source

NetCat TCP Source

  1. Netcat TCP Source监听TCP请求,在使用的时候需要监听指定的主机和端口,从这个指定主机的指定端口来接收TCP请求,并且将TCP请求内容作为日志来进行收集

  2. 默认情况下,每一条数据大小不能超过512B,可以通过参数max-line-length来修改

    1. 在Flume中,所有的流动模型,不是通过代码来指定,而是通过格式文件来配置,所以实际过程中,往往会要求格式文件存放在统一的位置上。上课过程中,统一要求将格式文件放到/opt/software/flume-1.11.0/data

      cd /opt/software/flume-1.11.0/
      mkdir data
      cd data
      
    2. 编辑格式文件,文件名和后缀名可以自己定义 properties文件的 key = value 格式配置在vim会有颜色上的区分,便于阅读。

      vim basic.propertie
      

      在文件中添加

      # 给Agent起名   a1就是这个agent的名字
      # 给Source起名
      # 如果有多个Source,那么名字之间用空格隔开
      a1.sources = s1
      # 给Channel起名
      a1.channels = c1
      # 给Sink起名
      a1.sinks = k1# 配置Source
      a1.sources.s1.type = netcat
      a1.sources.s1.bind = 0.0.0.0
      a1.sources.s1.port = 8090# 配置Channel
      a1.channels.c1.type = memory
      a1.channels.c1.capacity = 1000
      a1.channels.c1.transactionCapacity = 1000# 配置Sink
      a1.sinks.k1.type = logger# 将Source和Channel绑定
      a1.sources.s1.channels = c1
      # 将Sink和Channel绑定
      a1.sinks.k1.channel = c1
      
    3. 执行

      flume-ng agent --name a1 --conf $FLUME_HOME/conf --conf-file basic.properties -Dflume.root.logger=INFO,console
      
    4. 复制窗口之后,发送TCP请求

      nc hadoop01 8090
      

Exec Source

  1. Exec Source运行指定命令,监听命令结果,将命令结果作为日志进行收集

  2. 案例:监听指定文件,如果文件中新添了数据,那么自动收集这个文件中的数据

    1. 构建空文件

      touch /opt/software/flume-1.11.0/data/a.txt
      
    2. 监听这个文件,如果这个文件中新添了数据,自动收集数据作为日志

      vim execsource.properties
      

      在文件中添加

      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1# 配置Exec Source
      # Source的类型
      a1.sources.s1.type = exec
      # 监听指定的命令
      a1.sources.s1.command = tail -F /opt/software/flume-1.11.0/data/a.txt
      # 指定命令的脚本类型
      a1.sources.s1.shell = /bin/sh -ca1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      
    3. 执行命令

      flume-ng agent -n a1 -c $FLUME_HOME/conf -f execsource.properties -Dflume.root.logger=INFO,console
      
    4. 在新窗口中追加数据

      echo "testing" >> /opt/software/flume-1.11.0/data/a.txt
      echo "exec" >> /opt/software/flume-1.11.0/data/a.txt
      

      AVRO Source

      1. AVRO Source接收被AVRO序列化之后的数据,结合AVRO Sink,可以实现复杂的流动模型

      2. 案例

        1. 编辑文件

          cd /opt/software/flume-1.11.0/data/
          vim avrosource.properties 
          

          在文件中添加

          a1.sources = s1           
          a1.channels = c1
          a1.sinks = k1# 配置AVRO Source
          # 类型必须是avro
          a1.sources.s1.type = avro
          # 监听的主机
          a1.sources.s1.bind = 0.0.0.0
          # 监听的端口号
          a1.sources.s1.port = 6666a1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
          a1.sinks.k1.channel = c1
          
        2. 启动

          flume-ng agent -n a1 -c $FLUME_HOME/conf -f avrosource.properties -Dflume.root.logger=INFO,console
          
        3. 在新窗口中启动AVRO客户端

          flume-ng avro-client -H hadoop01 -p 6666 -F a.txt
          

      Spooling Directory Source

      1. 监听指定的目录,如果目录中产生了新的文件,那么自动的将新文件中的内容收集起来

      2. 默认情况下,这个文件如果被收集了,那么文件的后缀就是.COMPLETED

      3. 案例

        1. 创建目录

          mkdir /opt/flume_data
          
        2. 编辑文件

          vim spooldirsource.properties
          

          在文件中添加

          a1.sources = s1
          a1.channels = c1
          a1.sinks = k1# 配置Spooling Directory Source
          # 类型必须是spooldir
          a1.sources.s1.type = spooldir
          # 监听的目录
          a1.sources.s1.spoolDir = /opt/flume_data
          # 被收集过的文件后缀
          # 利用这条规则,可以过滤掉一部分不需要收集的文件
          a1.sources.s1.fileSuffix = .finisheda1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
          a1.sinks.k1.channel = c1
          
        3. 执行

          flume-ng agent -n a1 -c $FLUME_HOME/conf -f spooldirsource.properties -Dflume.root.logger=INFO,console
          

      Taildir Source

      1. 可以用于监听一个或者一组文件,如果被监听的文件中添加了新数据,那么新添的数据会被自动收集

      2. Exec Source需要通过指定tail -F命令才能监听指定文件,Spooling Directory Source监听指定的目录,并不能确定文件中是否新添了数据

      3. 不同于Exec Source的地方在于,Taildir Source不需要指定命令,还可以监控一类文件,且Taildir Source通过记录偏移量实现断点续传效果

      4. 偏移量通过属性positionFile来决定,默认是~/.flume/taildir_position.json

      5. 需要注意的是,Taildir Source不支持在Windows中使用

      6. 案例:监听flume_data目录下所有的log和txt文件,如果文件被添加新数据,那么自动收集

        1. 编辑文件

          vim taildirsource.properties
          
        2. 在文件中添加

          a1.sources = s1
          a1.channels = c1
          a1.sinks = k1# 配置Taildir Source
          # 类型必须是TAILDIR
          a1.sources.s1.type = TAILDIR
          # 监听的一组文件的组名
          a1.sources.s1.filegroups = f1 f2
          # 文件组中的要监听的文件
          a1.sources.s1.filegroups.f1 = /opt/flume_data/.*log.*
          a1.sources.s1.filegroups.f2 = /opt/flume_data/.*txt.*
          # 偏移量的存储位置
          a1.sources.s1.positionFile = /opt/flume_data/taildir_position.jsona1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
          a1.sinks.k1.channel = c1
          
        3. 执行

          flume-ng agent -n a1 -c $FLUME_HOME/conf -f taildirsource.properties -Dflume.root.logger=INFO,console
          

      Sequence Generator Source

      1. 序列产生器,从0开始递增到totalEvents,默认情况下totalEvents的值Long.MAX_VALUE

      2. 实际过程中,会利用这个Source测试流动模型是否搭建成功

      3. 案例

        a1.sources = s1
        a1.channels = c1
        a1.sinks = k1# 配置Sequence Generator Source
        # 类型必须是seq
        a1.sources.s1.type = seq
        # 最大值
        a1.sources.s1.totalEvents = 100a1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
        a1.sinks.k1.channel = c1
        

      HTTP Source

      1. 接收HTTP请求,并且将请求内容作为日志进行收集

      2. 只能接收GET和POST请求,其中GET请求接收只能用于实验,实际过程中使用HTTP Source来接收POST请求

      3. 案例

        1. 在文件中添加

          a1.sources = s1
          a1.channels = c1
          a1.sinks = k1# 配置HTTP Source
          # 类型必须是http
          a1.sources.s1.type = http
          # 监听端口
          a1.sources.s1.port = 8888a1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
          a1.sinks.k1.channel = c1
          
        2. 启动Flume

        3. 发送POST请求

          curl -X POST -d '[{"headers":{"class":"flume"},"body":"welcome~~~"}]' http://hadoop01:8888
          

      Custom Source

      1. Flume支持用户自定义Source。Flume针对Source提供了顶级接口Source,但是实际过程中,并不是实现Source接口,而是实现子接口之一:
        1. EventDrivenSource:事件驱动Source,本身是一个被动型Source,需要自己定义线程来获取数据以及封装数据
        2. PollableSource:拉取Source,本身是一个主动型Source,提供了线程来获取数据,只需要考虑数据怎么封装即可
      2. 由于在自定义Source的时候,还需要考虑获取格式文件中的参数值,所以还需要实现Configurable接口
      3. 实际过程中,考虑到要覆盖的方法比较多,所以继承AbstractSource
      pom依赖
      <!--Flume核心包-->
      <dependency><groupId>org.apache.flume</groupId><artifactId>flume-ng-core</artifactId><version>1.11.0</version>
      </dependency>
      <!--Flume开发包-->
      <dependency><groupId>org.apache.flume</groupId><artifactId>flume-ng-sdk</artifactId><version>1.11.0</version>
      </dependency>
      <!--Flume配置包-->
      <dependency><groupId>org.apache.flume</groupId><artifactId>flume-ng-configuration</artifactId><version>1.11.0</version>
      </dependency>
      
      自定义EventDrivenSource
      代码
      import org.apache.flume.Context;
      import org.apache.flume.Event;
      import org.apache.flume.EventDrivenSource;
      import org.apache.flume.channel.ChannelProcessor;
      import org.apache.flume.conf.Configurable;
      import org.apache.flume.event.EventBuilder;
      import org.apache.flume.source.AbstractSource;import java.util.HashMap;
      import java.util.Map;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;// 自定义代码实现Sequence Generator Source,加深理解
      public class AuthDrivenSource extends AbstractSource implements EventDrivenSource, Configurable {
      private long start;
      private long end;
      private long step;
      private ExecutorService es;// 获取参数值
      @Override
      public void configure(Context context) {// 3 -> +4// 获取起始值// 如果用户没有指定,那么默认从0开始递增start = context.getLong("start", 0L);// 获取结束值// 如果不指定,递增到Long.MAX_VALUEend = context.getLong("end", Long.MAX_VALUE);// 获取步长// 如果不指定,那么默认每次递增1step = context.getLong("step", 1L);// 保证数据的合理性if (start > end || step < 1)throw new IllegalArgumentException();
      }// 启动Source
      @Override
      public synchronized void start() {// 线程池中准备5个线程es = Executors.newFixedThreadPool(5);// 获取ChannelProcessorChannelProcessor cp = this.getChannelProcessor();// 提交任务es.submit(new Add(start, end, step, cp));
      }// 结束Source
      @Override
      public synchronized void stop() {if (es != null) es.shutdown();
      }}// 自增
      class Add implements Runnable {private final long start;
      private final long end;
      private final long step;
      private final ChannelProcessor cp;public Add(long start, long end, long step, ChannelProcessor cp) {this.start = start;this.end = end;this.step = step;this.cp = cp;
      }@Override
      public void run() {for (long i = start; i < end; i += step) {// 封装headers// 在headers中记录了数据产生的时间Map<String, String> headers = new HashMap<>();headers.put("time", String.valueOf(System.currentTimeMillis()));// 封装bodybyte[] body = String.valueOf(i).getBytes();// 将数据封装成EventEvent e = EventBuilder.withBody(body, headers);// 将Event传递给Channel来存储cp.processEvent(e);}
      }
      }
      
      1. 将程序打成jar包(要求是JDK1.8,好多框架还不支持17版本),上传到Flume安装目录的lib目录下

        cd /opt/software/flume-1.11.0/lib/
        rz
        
      2. 回到格式文件目录下,编辑文件

        cd /opt/software/flume-1.11.0/data/
        vim authdrivensource.properties
        

        在文件中添加

        a1.sources = s1
        a1.channels = c1
        a1.sinks = k1# 配置自定义EventDrivenSource
        # 类型必须是类的全路径名
        a1.sources.s1.type = com.fesco.source.AuthDrivenSource
        # 起始值
        a1.sources.s1.start = 10
        # 结束值
        a1.sources.s1.end = 100
        # 步长
        a1.sources.s1.step = 5a1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
        a1.sinks.k1.channel = c1
        
      3. 启动Flume

      自定义PollableSource
      代码
      import org.apache.flume.Context;
      import org.apache.flume.Event;
      import org.apache.flume.PollableSource;
      import org.apache.flume.channel.ChannelProcessor;
      import org.apache.flume.conf.Configurable;
      import org.apache.flume.event.EventBuilder;
      import org.apache.flume.source.AbstractSource;import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;public class AuthPollableSource extends AbstractSource implements PollableSource, Configurable {
      private long min;
      private long max;
      private long step;// 获取配置
      @Override
      public void configure(Context context) {this.min = context.getLong("min", 0L);this.max = context.getLong("max", Long.MAX_VALUE);this.step = context.getLong("step", 1L);if (min > max || step < 1)throw new IllegalArgumentException();
      }// 封装数据,写出数据
      @Override
      public Status process() {// 定义List来临时存储数据List<Event> events = new ArrayList<>();// 获取ChannelProcessorChannelProcessor cp = this.getChannelProcessor();for (long i = min; i < max; i += step) {// 封装headersMap<String, String> headers = new HashMap<>();headers.put("timestamp", String.valueOf(System.currentTimeMillis()));// 封装bodybyte[] body = String.valueOf(i).getBytes();// 封装EventEvent e = EventBuilder.withBody(body, headers);events.add(e);// 每50条数据写一次if (events.size() >= 50) {// 写出数据。这个方法一次写出多个cp.processEventBatch(events);// 清空集合events.clear();}}return Status.READY;
      }// PollableSource主动提供线程来获取数据
      // 如果线程暂时没有获取到数据,那么线程会临时休眠
      // 这个方法就是控制线程的休眠时间,单位是毫秒
      @Override
      public long getBackOffSleepIncrement() {return 1000;
      }@Override
      public long getMaxBackOffSleepInterval() {return 10000;
      }
      }
      
      1. 打成jar包,上传到lib目录下

        cd ../lib
        rz
        
      2. 回到格式文件目录下,编辑文件

        cd ../data/
        vim authpollablesource.properties
        

        在文件中添加

        a1.sources = s1
        a1.channels = c1
        a1.sinks = k1# 配置自定义PollableSource
        # 类型必须是类的全路径名
        a1.sources.s1.type = com.fesco.source.AuthPollableSource   //注意自己的全类名
        # 起始值
        a1.sources.s1.min = 10
        # 结束值
        a1.sources.s1.max = 1000
        # 步长
        a1.sources.s1.step = 5a1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
        a1.sinks.k1.channel = c1
        

      cd …/data/
      vim authpollablesource.properties

      
      在文件中添加```properties
      a1.sources = s1
      a1.channels = c1
      a1.sinks = k1# 配置自定义PollableSource
      # 类型必须是类的全路径名
      a1.sources.s1.type = com.fesco.source.AuthPollableSource   //注意自己的全类名
      # 起始值
      a1.sources.s1.min = 10
      # 结束值
      a1.sources.s1.max = 1000
      # 步长
      a1.sources.s1.step = 5a1.channels.c1.type = memorya1.sinks.k1.type = loggera1.sources.s1.channels = c1
      a1.sinks.k1.channel = c1
      
      1. 启动Flume

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

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

相关文章

RIP,EIGRP,OSPF的区别

1.路由协议 能否选择出最优路径 2.路由协议 是否能够完成故障切换/多久能够完成故障切换 3.路由协议 是否会占用过大硬件资源 -- RIP -- 路由信息协议 跳数:一次三层设备的转发算一跳 中间隔的设备数量 不按照链路带宽来算 Rip认为路径一样,这个时候。 下面这个跳数不…

【C语言_数组_复习篇】

目录 一、数组的概念 二、数组的类型 三、一维数组 3.1 一维数组的创建 3.2 一维数组的初始化 3.3 一维数组的访问 3.4 一维数组在内存中的存储 四、二维数组 4.1 二维数组的创建 4.2 二维数组的初始化 4.3 二维数组的访问 4.4 二维数组在内存中的存储 五、变长数组 六、…

kafka面试篇

消息队列的作用&#xff1a;异步、削峰填谷、解耦 高可用&#xff0c;几乎所有相关的开源软件都支持&#xff0c;满足大多数的应用场景&#xff0c;尤其是大数据和流计算领域&#xff0c; kafka高效&#xff0c;可伸缩&#xff0c;消息持久化。支持分区、副本和容错。 对批处理…

ChatGPT论文指南|揭秘8大ChatGPT提示词研究技巧提升写作效率【建议收藏】

点击下方▼▼▼▼链接直达AIPaperPass &#xff01; AIPaperPass - AI论文写作指导平台 公众号原文▼▼▼▼&#xff1a; ChatGPT论文指南|揭秘8大ChatGPT提示词研究技巧提升写作效率【建议收藏】 目录 1.写作方法 2.方法设计 3.研究结果 4.讨论写作 5.总结结论 6.书…

常见技术难点及方案

1. 分布式锁 1.1 难点 1.1.1 锁延期 同一时间内不允许多个客户端同时获得锁&#xff1b; 1.1.2 防止死锁 需要确保在任何故障场景下&#xff0c;都不会出现死锁&#xff1b; 1.2.3 可重入 特殊的锁机制&#xff0c;它允许同一个线程多次获取同一个锁而不会被阻塞。 1.2…

【Linux】HTTP协议 HTTPS协议

jsoncpp库的安装使用 sudo yum install jsoncpp-devel 使用jsoncpp包含头文件<jsoncpp/json/json.h> Makefile要添加链接jsoncpp库的选项-ljsoncpp HTTP协议 应用层&#xff1a;通俗说就是程序员在socket接口之上编写的具体逻辑&#xff0c;其中很多工作都是和文本处理…

蓝桥杯需要掌握的几个案例(C/C++)

文章目录 蓝桥杯C/C组的重点主要包括以下几个方面&#xff1a;以下是一些在蓝桥杯C/C组比赛中可能会涉及到的重要案例类型&#xff1a;1. **排序算法案例**&#xff1a;2. **查找算法案例**&#xff1a;3. **数据结构案例**&#xff1a;4. **动态规划案例**&#xff1a;5. **图…

Linux的一些基本指令

​​​​​​​ 目录 前言&#xff1a; 1.以指令的形式登录 2.ls指令 语法&#xff1a; 功能&#xff1a; 常用选项&#xff1a; 3.pwd指令 4.cd指令 4.1 绝对路径与相对路径 4.2 cd .与cd ..&#xff08;注意cd后先空格&#xff0c;然后两个点是连一起的&#xff0…

选择word中的表格VBA

打开开发工具 选择Visual Basic插入代码 Sub 选择word中的表格() Dim t As Table an MsgBox("即将选择选区内所有表格&#xff0c;若无选区&#xff0c;则选择全文表格。", vbYesNo, "提示") If an - 6 Then Exit Sub Set rg IIf(Selection.Type wdSel…

33-Java服务定位器模式 (Service Locator Pattern)

Java服务定位器模式 实现范例 服务定位器模式&#xff08;Service Locator Pattern&#xff09;用于想使用 JNDI 查询定位各种服务的时候考虑到为某个服务查找 JNDI 的代价很高&#xff0c;服务定位器模式充分利用了缓存技术在首次请求某个服务时&#xff0c;服务定位器在 JNDI…

return code 1 from org.apache.hadoop.hive.ql.ddl.DDLTask

Bug信息 Error: Error while compiling statement: FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.ddl.DDLTask (state=08S01,code=1)Bug产生的代码 修复hive表分区: msck repair table xxxBug原因排查 分区数量过大 这个是网上查看的说如果一次…

(三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练

这里写目录标题 一、colmap解算数据放入高斯1. 将稀疏重建的文件放入高斯2. 将稠密重建的文件放入高斯 二、vkitti数据放入高斯 一、colmap解算数据放入高斯 运行Colmap.bat文件之后&#xff0c;进行稀疏重建和稠密重建之后可以得到如下文件结构。 1. 将稀疏重建的文件放入高…

02.percona Toolkit工具pt-archiver命令实践

1.命令作用 Percona Toolkit有的32个命令&#xff0c;可以分为7大类 工具类别 工具命令 工具作用 备注 开发类 pt-duplicate-key-checker 列出并删除重复的索引和外键 pt-online-schema-change 在线修改表结构 pt-query-advisor 分析查询语句&#xff0c;并给出建议&#x…

AST学习入门

AST学习入门 1.AST在线解析网站 https://astexplorer.net/ 1.type: 表示当前节点的类型&#xff0c;我们常用的类型判断方法t.is********(node)**,就是判断当前的节点是否为某个类型。 2**.start**:表示当前节点的开始位置 3.end:当前节点结束 4.loc : 表示当前节点所在的行…

项目解决方案:旅游景区4G/5G无线视频监控联网系统设计方案

目录 一、背景 二、系统设计 1.1 总体设计要求 1.2 系统架构设计说明 1.3 系统拓扑图 1.4 关键技术 1.4.1 5G支持技术 1.4.2 视频图像处理技术 1.4.3 数据融合与分析技术 三、功能特点 3.1 高效可靠 3.2 实时监测 3.3 远程控制 3.4 故障预测 四、应用前景 …

DBO优化GRNN回归预测(matlab代码)

DBO-GRNN回归预测matlab代码 蜣螂优化算法(Dung Beetle Optimizer, DBO)是一种新型的群智能优化算法&#xff0c;在2022年底提出&#xff0c;主要是受蜣螂的的滚球、跳舞、觅食、偷窃和繁殖行为的启发。 数据为Excel股票预测数据。 数据集划分为训练集、验证集、测试集,比例…

面试笔记——Redis(双写一致、持久化)

双写一致 双写一致性&#xff1a; 当修改了数据库中的数据&#xff0c;也要更新缓存的数据&#xff0c;使缓存和数据库中的数据保持一致。 相关问题&#xff1a;使用Redis作为缓存&#xff0c;mysql的数据如何与Redis进行同步&#xff1f;——双写一致性问题 回答时&#xff0…

五、分布式锁-redission

源码仓库地址&#xff1a;gitgitee.com:chuangchuang-liu/hm-dingping.git 1、redission介绍 目前基于redis的setnx特性实现的自定义分布式锁仍存在的问题&#xff1a; 问题描述重入问题同一个线程无法多次获取统一把锁。当方法A成功获取锁后&#xff0c;调用方法B&#xff0…

说说webpack中常见的Loader?解决了什么问题?

文章目录 一、是什么配置方式 二、特性三、常见的loadercss-loaderstyle-loaderless-loaderraw-loaderfile-loaderurl-loader 参考文献 一、是什么 loader 用于对模块的"源代码"进行转换&#xff0c;在 import 或"加载"模块时预处理文件 webpack做的事情…

【JS】如何避免输入中文拼音时触发input事件

现有一段代码&#xff0c;监听input事件。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" con…