Kafka实时数据即席查询应用与实践

作者:vivo 互联网搜索团队- Deng Jie

 

Kafka中的实时数据是以Topic的概念进行分类存储,而Topic的数据是有一定时效性的,比如保存24小时、36小时、48小时等。而在定位一些实时数据的Case时,如果没有对实时数据进行历史归档,在排查问题时,没有日志追述,会很难定位是哪个环节的问题。

一、背景

Kafka中的实时数据是以Topic的概念进行分类存储,而Topic的数据是有一定时效性的,比如保存24小时、36小时、48小时等。而在定位一些实时数据的Case时,如果没有对实时数据进行历史归档,在排查问题时,没有日志追述,会很难定位是哪个环节的问题。因此,我们需要对处理的这些实时数据进行记录归档并存储。

二、内容

2.1 案例分析

这里以i视频和vivo短视频实时数据为例,之前存在这样的协作问题:

数据上游内容方提供实时Topic(存放i视频和vivo短视频相关实时数据),数据侧对实时数据进行逻辑处理后,发送给下游工程去建库实时索引,当任务执行一段时间后,工程侧建索引偶尔会提出数据没有发送过去的Case,前期由于没有对数据做存储,在定位问题的时候会比较麻烦,经常需求查看实时日志,需要花费很长的时间来分析这些Case是出现在哪个环节。

 

为了解决这个问题,我们可以将实时Topic中的数据,在发送给其他Topic的时候,添加跟踪机制,进行数据分流,Sink到存储介质(比如HDFS、Hive等)。这里,我们选择使用Hive来进行存储,主要是查询方便,支持SQL来快速查询。如下图所示:

图片

在实现优化后的方案时,有两种方式可以实现跟踪机制,它们分别是Flink SQL写Hive、Flink DataStream写Hive。接下来,分别对这两种实现方案进行介绍和实践。

2.2 方案一:Flink SQL写Hive

这种方式比较直接,可以在Flink任务里面直接操作实时Topic数据后,将消费后的数据进行分流跟踪,作为日志记录写入到Hive表中,具体实现步骤如下:

  • 构造Hive Catalog;

  • 创建Hive表;

  • 写入实时数据到Hive表。

2.2.1 构造Hive Catalog

在构造Hive Catalog时,需要初始化Hive的相关信息,部分代码片段如下所示:

// 设置执行环境
 StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
 EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().build();
 StreamTableEnvironment tEnv = StreamTableEnvironment.create(env,settings);
 
 // 构造 Hive Catalog 名称
 String name = "video-hive-catalog";
 // 初始化数据库名
 String defaultDatabase = "comsearch";
 // Hive 配置文件路径地址
 String hiveConfDir = "/appcom/hive/conf";
 // Hive 版本号
 String version = "3.1.2";
 
 // 实例化一个 HiveCatalog 对象
 HiveCatalog hive = new HiveCatalog(name, defaultDatabase, hiveConfDir, version);
 // 注册HiveCatalog
 tEnv.registerCatalog(name, hive);
 // 设定当前 HiveCatalog
 tEnv.useCatalog(name);
 // 设置执行SQL为Hive
 tEnv.getConfig().setSqlDialect(SqlDialect.HIVE);
 // 使用数据库
 tEnv.useDatabase("db1");

在以上代码中,我们首先设置了 Flink 的执行环境和表环境,然后创建了一个 HiveCatalog,并将其注册到表环境中。

2.2.2 创建Hive表

如果Hive表不存在,可以通过在程序中执行建表语句,具体SQL见表语句代码如下所示:

-- 创建表语句 
tEnv.executeSql("CREATE TABLE IF NOT EXISTS TABLE `xxx_table`(
  `content_id` string,
  `status` int)
PARTITIONED BY (
  `dt` string,
  `h` string,
  `m` string)
stored as ORC
TBLPROPERTIES (
  'auto-compaction'='true',
  'sink.partition-commit.policy.kind'='metastore,success-file',
  'partition.time-extractor.timestamp-pattern'='$dt $h:$m:00'
)")

在创建Hive表时我们使用了IF NOT EXISTS关键字,如果Hive中该表不存在会自动在Hive上创建,也可以提前在Hive中创建好该表,Flink SQL中就无需再执行建表SQL,因为用了Hive的Catalog,Flink SQL运行时会找到表。这里,我们设置了auto-compaction属性为true,用来使小文件自动合并,1.12版的新特性,解决了实时写Hive产生的小文件问题。同时,指定metastore值是专门用于写入Hive的,也需要指定success-file值,这样CheckPoint触发完数据写入磁盘后会创建_SUCCESS文件以及Hive metastore上创建元数据,这样Hive才能够对这些写入的数据可查。

2.2.3 写入实时数据到Hive表

在准备完成2.2.1和2.2.2中的步骤后,接下来就可以在Flink任务中通过SQL来对实时数据进行操作了,具体实现代码片段如下所示:

// 编写业务SQL
 String insertSql = "insert into  xxx_table SELECT content_id, status, " +
                    " DATE_FORMAT(ts, 'yyyy-MM-dd'), DATE_FORMAT(ts, 'HH'), DATE_FORMAT(ts, 'mm') FROM xxx_rt";
 // 执行 Hive SQL
 tEnv.executeSql(insertSql);
 // 执行任务
 env.execute();

将消费后的数据进行分类,编写业务SQL语句,将消费的数据作为日志记录,发送到Hive表进行存储,这样Kafka中的实时数据就存储到Hive了,方便使用Hive来对Kafka数据进行即席分析。

2.2.4 避坑技巧

使用这种方式在处理的过程中,如果配置使用的是EventTime,在程序中配置'sink.partition-commit.trigger'='partition-time',最后会出现无法提交分区的情况。经过对源代码PartitionTimeCommitTigger的分析,找到了出现这种异常情况的原因。

我们可以通过看

org.apache.flink.table.filesystem.stream.PartitionTimeCommitTigger#committablePartitionsorg.apache.flink.table.filesystem.stream.PartitionTimeCommitTigger#committablePartitions

中的一个函数,来说明具体的问题,部分源代码片段如下:

// PartitionTimeCommitTigger源代码函数代码片段
@Override
public List<String> committablePartitions(long checkpointId) {
 if (!watermarks.containsKey(checkpointId)) {
  throw new IllegalArgumentException(String.format(
    "Checkpoint(%d) has not been snapshot. The watermark information is: %s.",
    checkpointId, watermarks));
 }
 
 long watermark = watermarks.get(checkpointId);
 watermarks.headMap(checkpointId, true).clear();
 
 List<String> needCommit = new ArrayList<>();
 Iterator<String> iter = pendingPartitions.iterator();
 while (iter.hasNext()) {
  String partition = iter.next();
  // 通过分区的值来获取分区的时间
  LocalDateTime partTime = extractor.extract(
    partitionKeys, extractPartitionValues(new Path(partition)));
  // 判断水印是否大于分区创建时间+延迟时间
  if (watermark > toMills(partTime) + commitDelay) {
   needCommit.add(partition);
   iter.remove();
  }
 }
 return needCommit;
}

通过分析上述代码片段,我们可以知道系统通过分区值来抽取相应的分区来创建时间,然后进行比对,比如我们设置的时间 pattern 是 '$dt $h:$m:00' , 某一时刻我们正在往 /2022-02-26/18/20/ 这个分区下写数据,那么程序根据分区值,得到的 pattern 将会是2022-02-26 18:20:00,这个值在SQL中是根据 DATA_FORMAT 函数获取的。

而这个值是带有时区的,比如我们的时区设置为东八区,2022-02-26 18:20:00这个时间是东八区的时间,换成标准 UTC 时间是减去8个小时,也就是2022-02-26 10:20:00,而在源代码中的 toMills 函数在处理这个东八区的时间时,并没有对时区进行处理,把这个其实应该是东八区的时间当做了 UTC 时间来处理,这样计算出来的值就比实际值大8小时,导致一直没有触发分区的提交。

如果我们在数据源中构造的分区是 UTC 时间,也就是不带分区的时间,那么这个逻辑就是没有问题的,但是这样又不符合我们的实际情况,比如对于分区2022-02-26 18:20:00,我希望我的分区肯定是东八区的时间,而不是比东八区小8个小时的UTC时间2022-02-26 10:20:00。

在明白了原因之后,我们就可以针对上述异常情况进行优化我们的实现方案,比如自定义一个分区类、或者修改缺省的时间分区类。比如,我们使用TimeZoneTableFunction类来实现一个自定义时区,部分参考代码片段如下:

public class CustomTimeZoneTableFunction implements TimeZoneTableFunction {
 
  private transient DateTimeFormatter formatter;
  private String timeZoneId;
 
  public CustomTimeZoneTableFunction(String timeZoneId) {
    this.timeZoneId = timeZoneId;
  }
 
  @Override
  public void open(FunctionContext context) throws Exception {
    // 初始化 DateTimeFormatter 对象
    formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:00");
    formatter = formatter.withZone(ZoneId.of(timeZoneId));
  }
 
  @Override
  public void eval(Long timestamp, Collector<TimestampWithTimeZone> out) {
    // 将时间戳转换为 LocalDateTime 对象
    LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), ZoneOffset.UTC);
    // 将 LocalDateTime 对象转换为指定时区下的 LocalDateTime 对象
    LocalDateTime targetDateTime = localDateTime.atZone(ZoneId.of(timeZoneId)).toLocalDateTime();
    // 将 LocalDateTime 对象转换为 TimestampWithTimeZone 对象,并输出到下游
    out.collect(TimestampWithTimeZone.fromLocalDateTime(targetDateTime, ZoneId.of(timeZoneId)));
  }
}

2.3 方案二:Flink DataStream写Hive

在一些特殊的场景下,Flink SQL如果无法实现我们复杂的业务需求,那么我们可以考虑使用Flink DataStream写Hive这种实现方案。比如如下业务场景,现在需要实现这样一个业务需求,内容方将实时数据写入到Kafka消息队列中,然后由数据侧通过Flink任务消费内容方提供的数据源,接着对消费的数据进行分流处理(这里的步骤和Flink SQL写Hive的步骤类似),每分钟进行存储到HDFS(MapReduce任务需要计算和重跑HDFS数据),然后通过MapReduce任务将HDFS上的这些日志数据生成Hive所需要格式,最后将这些Hive格式数据文件加载到Hive表中。实现Kafka数据到Hive的即席分析功能,具体实现流程细节如下图所示:

图片

 

具体核心实现步骤如下:

  • 消费内容方Topic实时数据;

  • 生成数据预处理策略;

  • 加载数据;

  • 使用Hive SQL对Kafka数据进行即席分析。

2.3.1 消费内容方Topic实时数据

编写消费Topic的Flink代码,这里不对Topic中的数据做逻辑处理,在后面统一交给MapReduce来做数据预处理,直接消费并存储到HDFS上。具体实现代码如下所示:

public class Kafka2Hdfs {
 
    public static void main(String[] args) {
        // 判断参数是否有效
        if (args.length != 3) {
            LOG.error("kafka(server01:9092), hdfs(hdfs://cluster01/data/), flink(parallelism=2) must be exist.");
            return;
        }
        // 初始化Kafka连接地址和HDFS存储地址以及Flink并行度
        String bootStrapServer = args[0];
        String hdfsPath = args[1];
        int parallelism = Integer.parseInt(args[2]);
 
        // 实例化一个Flink任务对象
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        env.enableCheckpointing(5000);
        env.setParallelism(parallelism);
        env.setStreamTimeCharacteristic(TimeCharacteristic.EventTime);
 
        // Flink消费Topic中的数据
        DataStream<String> transction = env.addSource(new FlinkKafkaConsumer010<>("test_bll_topic", new SimpleStringSchema(), configByKafkaServer(bootStrapServer)));
 
        // 实例化一个HDFS存储对象
        BucketingSink<String> sink = new BucketingSink<>(hdfsPath);
 
        // 自定义存储到HDFS上的文件名,用小时和分钟来命名,方便后面算策略
        sink.setBucketer(new DateTimeBucketer<String>("HH-mm"));
 
        // 设置存储HDFS的文件大小和存储文件时间频率
        sink.setBatchSize(1024 * 1024 * 4);
        sink.setBatchRolloverInterval(1000 * 30);
        transction.addSink(sink);
 
        env.execute("Kafka2Hdfs");
    }
 
    // 初始化Kafka对象连接信息
    private static Object configByKafkaServer(String bootStrapServer) {
        Properties props = new Properties();
        props.setProperty("bootstrap.servers", bootStrapServer);
        props.setProperty("group.id", "test_bll_group");
        props.put("enable.auto.commit", "true");
        props.put("auto.commit.interval.ms", "1000");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        return props;
    }
 
}

注意事项:

  • 这里我们把时间窗口设置小一些,每30s做一次Checkpoint,如果该批次的时间窗口没有数据过来,就生成一个文件落地到HDFS上;

  • 另外,我们重写了Bucketer为DateTimeBucketer,逻辑并不复杂,在原有的方法上加一个年-月-日/时-分的文件生成路径,例如在HDFS上的生成路径:xxxx/2022-02-26/00-00。

具体DateTimeBucketer实现代码如下所示:

public class DateMinuteBucketer implements Bucketer<String> {
    private SimpleDateFormat baseFormatDay = new SimpleDateFormat("yyyy-MM-dd");
    private SimpleDateFormat baseFormatMin = new SimpleDateFormat("HH-mm");
 
    @Override
    public Path getBucketPath(Clock clock, Path basePath, String element) {
        return new Path(basePath + "/" + baseFormatDay.format(new Date()) + "/" + baseFormatMin.format(new Date()));
    }
}

2.3.2 生成数据预处理策略

这里,我们需要对落地到HDFS上的文件进行预处理,处理的逻辑是这样的。比如,现在是2022-02-26 14:00,那么我们需要将当天的13:55,13:56,13:57,13:58,13:59这最近5分钟的数据处理到一起,并加载到Hive的最近5分钟的一个分区里面去。那么,我们需要生成这样一个逻辑策略集合,用HH-mm作为key,与之最近的5个文件作为value,进行数据预处理合并。具体实现代码步骤如下:

  • 步骤一:获取小时循环策略;

  • 步骤二:获取分钟循环策略;

  • 步骤三:判断是否为5分钟的倍数;

  • 步骤四:对分钟级别小于10的数字做0补齐(比如9补齐后变成09);

  • 步骤五:对小时级别小于10的数字做0补齐(比如1补齐后变成01);

  • 步骤六:生成时间范围;

  • 步骤七:输出结果。

其中,主要的逻辑是在生成时间范围的过程中,根据小时和分钟数的不同情况,生成不同的时间范围,并输出结果。在生成时间范围时,需要注意前导0的处理,以及特殊情况(如小时为0、分钟为0等)的处理。最后,将生成的时间范围输出即可。

根据上述步骤编写对应的实现代码,生成当天所有日期命名规则,预览部分结果如下:

图片

需要注意的是,如果发生了第二天00:00,那么我们需要用到前一天的00-00=>23-59,23-58,23-57,23-56,23-55这5个文件中的数据来做预处理。

2.3.3 加载数据

在完成2.3.1和2.3.2里面的内容后,接下来,我们可以使用Hive的load命令直接加载HDFS上预处理后的文件,把数据加载到对应的Hive表中,具体实现命令如下:

-- 加载数据到Hive表
load data inpath '<hdfs_path_hfile>' overwrite into table xxx.table partition(day='2022-02-26',hour='14',min='05')

2.3.4 即席分析

之后,我们使用Hive SQL来对Kafka数据进行即席分析,示例SQL如下所示:

-- 查询某5分钟分区数据
select * from xxx.table where day='2022-02-26' and hour='14' and min='05'

2.4 Flink SQL与 Flink DataStream如何选择

Flink SQL 和 Flink DataStream 都是 Flink 中用于处理数据的核心组件,我们可以根据自己实际的业务场景来选择使用哪一种组件。

Flink SQL 是一种基于 SQL 语言的数据处理引擎,它可以将 SQL 查询语句转换为 Flink 的数据流处理程序。相比于 Flink DataStream,Flink SQL 更加易于使用和维护,同时具有更快的开发速度和更高的代码复用性。Flink SQL 适用于需要快速开发和部署数据处理任务的场景,比如数据仓库、实时报表、数据清洗等。

Flink DataStream API是Flink数据流处理标准API,SQL是Flink后期版本提供的新的数据处理操作接口。SQL的引入为提高了Flink使用的灵活性。可以认为Flink SQL是一种通过字符串来定义数据流处理逻辑的描述语言。

因此,在选择 Flink SQL 和 Flink DataStream 时,需要根据具体的业务需求和数据处理任务的特点来进行选择。如果需要快速开发和部署任务,可以选择使用 Flink SQL;如果需要进行更为深入和定制化的数据处理操作,可以选择使用 Flink DataStream。同时,也可以根据实际情况,结合使用 Flink SQL 和 Flink DataStream 来完成复杂的数据处理任务。

三、 总结

在实际应用中,Kafka实时数据即席查询可以用于多种场景,如实时监控、实时报警、实时统计、实时分析等。具体应用和实践中,需要注意以下几点:

  • 数据质量:Kafka实时数据即席查询需要保证数据质量,避免数据重复、丢失或错误等问题,需要进行数据质量监控和调优。

  • 系统复杂性:Kafka实时数据即席查询需要涉及到多个系统和组件,包括Kafka、数据处理引擎(比如Flink)、查询引擎(比如Hive)等,需要对系统进行配置和管理,增加了系统的复杂性。

  • 安全性:Kafka实时数据即席查询需要加强数据安全性保障,避免数据泄露或数据篡改等安全问题,做好Hive的权限管控。

  • 性能优化:Kafka实时数据即席查询需要对系统进行性能优化,包括优化数据处理引擎、查询引擎等,提高系统的性能和效率。

 

参考:

  1. http://github.com/apache/flink

  2. http://flink.apache.org/

分享 vivo 互联网技术干货与沙龙活动,推荐最新行业动态与热门会议。
本文转载于网络 如有侵权请联系删除

相关文章

  • Go Web---tcp服务器

    GoWeb---tcp服务器tcp服务器优化版本tcp服务器这部分我们将使用TCP协议和之前讲到的协程范式编写一个简单的客户端-服务器应用,一个(web)服务器应用需要响应众多客户端的并发请求:Go会为每一个客户端产生一个协程用来处理请求。我们需要使用net包中网络通信的功能。它包含了处理TCP/IP以及UDP协议、域名解析等方法。服务器端代码是一个单独的文件:packagemain import( "fmt" "net" ) funcmain(){ fmt.Println("Startingtheserver...") //创建listener listener,err:=net.Listen("tcp","localhost:50000") iferr!=nil{ fmt.Println("Errorlistening",err.Error()) return//终止程序 } //监听并接受来自客户端的连接 for{ con

  • CAMoE——屠榜 video retrieval challenge

    作者:炎思提原文:https://zhuanlan.zhihu.com/p/425226244c ✎编者言来自今年九月arXiv上一篇关于视频检索文章。作者来自国科大和快手,提出CAMoE模型,投稿AAAI。实验结果非常恐怖,在几乎所有主流数据集上达到SoTA,其中videoretrievalchallenge刷榜6个数据集,而文章所作改变非常小,主体上仅修改了loss,而且代码量仅1行。本文工作我认为非常有意义,因此将详细描述文章各个模块和步骤,最后给出思考。详细信息如下:论文名称:ImprovingVideo-TextRetrievalbyMulti-StreamCorpusAlignmentandDualSoftmaxLoss论文链接:https://arxiv.org/abs/2109.04290项目链接:https://github.com/starmemda/CAMoE/videoretrievalchallenge 01Overallstructure 语义方面:使用POS和SGS分别解析语句和整合语句,应用CLIP预训练的Bert进行语义提取。这里的Bert应该是CL

  • 如何写好一个 Spring 组件

    背景Spring框架提供了许多接口,可以使用这些接口来定制化bean,而非简单的getter/setter或者构造器注入。细翻SpringCloudNetflix、SpringCloudAlibaba等这些构建在SpringFramework的成熟框架源码,你会发现大量的扩展bean例如Eureka健康检查packageorg.springframework.cloud.netflix.eureka; publicclassEurekaHealthCheckHandlerimplementsInitializingBean{}复制SeataFeign配置packagecom.alibaba.cloud.seata.feign; publicclassSeataContextBeanPostProcessorimplementsBeanPostProcessor{}复制代码示例DemoBean@Slf4j publicclassDemoBeanimplementsInitializingBean{ publicDemoBean(){ log.info("-->

  • 计算语言顶会ACL 2018最佳论文公布!这些大学与研究员榜上有名

    机器之心整理机器之心编辑部今日,ACL2018公布了5篇最佳论文,包括三篇最佳长论文和2篇最佳短论文。今年的ACL共收到1544份提交论文,其中1018份长论文接收了258篇,526份短论文接收了126篇,总体接受率为24.9%。ACL2018获奖名单如下:最佳长论文1.Findingsyntaxinhumanencephalographywithbeamsearch(尚未公开)作者:JohnHale、ChrisDyer、AdhigunaKuncoro、JonathanBrennan2.LearningtoAskGoodQuestions:RankingClarificationQuestionsusingNeuralExpectedValueofPerfectInformation作者:SudhaRao、HalDauméIII均来自于马里兰大学帕克分校。3.Let’sdoit“again”:AFirstComputationalApproachtoDetectingAdverbialPresuppositionTriggers作者:AndreCianflone、YulanFeng、J

  • 新三巨头的AI战场:头条颠覆,美团赋能,滴滴深耕

  • centos6.6 下安装mysql

    背景没啥好说的,就是需要搭建自己的测试数据库跟研发的数据隔离开来,需要怼mysql步骤1.确认线上mysql的版本SELECTVERSION();复制2.确认安装方式,其实yum命令也可以直接安装,厉害了,但这样安装的话,配置不方便,一般不推荐,直接用tar.gz包来安装吧3.创建源包目录mkdir-p/home/jwen/local/mysql-5.7.12复制4. 下载源wgethttp://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.12-linux-glibc2.5-x86_64.tar复制5.解压tarxvfmysql-5.7.12-linux-glibc2.5-x86_64.tar复制6.解压mysql包tar-xzvfmysql-5.7.12-linux-glibc2.5-x86_64.tar.gz复制7.创建软链接ln-s/home/jwen/local/mysql-5.7.12/mysql-5.7.12-linux-glibc2.5-x86_64/home/jwen/local/mysql复制8.创建数据库目录mkd

  • 腾讯云消息队列CMQ版主题管理

    操作场景主题模型类似设计模式中的发布订阅模式,Topic相当于发布消息的单位,Topic下面的订阅者相当于观察者模式。Topic会把发布的消息主动推送给订阅者。 本文档介绍在使用TDMQCMQ版时,如何创建或删除一个主题。 操作步骤创建主题 登录TDMQCMQ版控制台。 在左侧导航栏选择主题订阅,选择好地域,单击新建,根据页面提示填写信息。 主题名称:以字母起始,只能包含字母、数字、“-”及“_”,最大64字符,创建后不能修改,不区分大小写。 消息堆积:未触发推送到订阅者,或订阅者接收失败的消息,暂时堆积到主题中。 消息过滤类型: 标签:CMQ提供生产、订阅的消息标签匹配能力,可用于消息过滤。详细规则参见标签键匹配功能说明。 路由匹配:Bindingkey、Routingkey是组合使用的,完全兼容rabbitmqtopic匹配模式。发消息时配的Routingkey是客户端发消息带的。创建订阅关系时配的Bindingkey是topic和订阅者的绑定关系。详细规则请参见路由键匹配功能说明。 资源标签:选填,标签可以帮助您从各种维度方便地对TDMQCMQ版资源进行分类管理,具体使用方法

  • kubernetes之Deployment控制器(一)

    1.什么是Deployment? Deployment(简写为deploy)是kubernetes控制器的又一种实现,构建于ReplicasSet控制器之上,可以为Pod和ReplicaSet提供声明式更新。相比较而言,Pod和ReplicaSet很少用来直接使用,而是借助于控制器来使用。DeploymentController核心功能也是保证Pod资源的正常使用,大部分功能调用ReplicaSet来实现。 1.2我们只需要描述Deployment中目标Pod期望状态,而Deployment控制器以控制更改为实际状态,使其变成期望状态。我们不需要直接使用Pod和ReplicaSet来实现,Deployment控制器在ReplicaSet的基础上增加了部分特性: 1.事件和状态查看:可以通过特定的命令查看Deployment对象的更新进度和状态; 2.版本记录:将Deployment对象的历史更新操作都进行保存,以便于后续执行回滚操作使用; 3.多种更新方案:Recreate重建,可以实现单批次更新所有的Pod。RollingUpdate可以实现多批次替换Pod至新版本。 2.Deplo

  • 三、K3 Cloud 开发插件《K3 Cloud插件开发新手指导 + K3 Cloud插件开发代码调试》

    案例需求:在销售订单上新增一个按钮,在订单明细中新增一个字段,命名[即时库存]。 点击按钮,弹出“HelloWorld!”,并获取订单明细物料的即时库存,填入字段[即时库存]。 开发工具:VisualStudio2012 开发语言:Asp.netC# ================================= 目录: 1、BOS单据加按钮--【测试按钮】 2、BOS单据加字段--[即时库存] 3、创建VisualC#类库 4、引入命名空间 5、编写按钮点击事件,建议用不带任何代码的空白事件进行测试 6、编译代码,生成DLL文件 7、插件注册 8、IIS重启 9、代码调试,确保事件是否成功触发 10、代码调试过程分析 ================================= 1、BOS单据加按钮--【测试按钮】  打开【金蝶K3CloudBOS集成开发平台】,右键点击【销售订单】,选择扩展(在扩展/继承模板才可加字段), 在【销售订单属性】窗口,点击【菜单集合】,在单据头新增按钮【测试按钮】。   在菜单编辑中,右键点击【工具条】,新增按钮【测试按

  • Win7下的内置FTP组件的设置详解

    本文摘自:https://www.cnblogs.com/grenet/archive/2012/05/04/2480682.html 在局域网中共享文件,FTP是比较方便的方案之一。Win7内部集成了FTP,只是设置起来颇费一番功夫。着文以记之。   一、安装FTP组件     由于Win7默认没有安装FTP组件。故FTP的设置第一步就是安装FTP组件     点击:控制面板—》程序和功能—》打开或关闭Windows功能。勾选“FTP服务器”及“FTP服务”“FTP扩展性”,点击“确定”,安装FTP组件。如下图所示        二、添加FTP站点     点击:控制面板—》管理工具。选中“Internet信息服务(IIS)管理器”,如图          双击“Internet信息服务(IIS)管理器”。弹出管理器界面,如下图所示:          单击选中“网站”,并且在其上右击,选择“添加FTP站点”,出现“站点信息”界面,如下所示:          给FTP取名(本例是:zhu),以及设置FTP站点的物理路径(本例是:c:\ftp),点击“下一步”,出现“绑定和SSL设置

  • PCB特征阻抗计算

    见教程:链接:https://pan.baidu.com/s/1V4UbEoKfMD1bilwu-Qwdyg密码:ml6t  

  • Shell echo命令

    显示变量 read命令从标准输入中读取一行,并把输入行的每个字段的值指定给shell变量 #!/bin/sh readname echo"$nameItisatest"复制 以上代码保存为test.sh,name接收标准输入的变量,结果将是: [root@www~]#shtest.sh OK          #标准输入 OKItisatest    #输出 显示换行 echo-e"OK!\n"#-e开启转义 echo"Ititatest"复制 输出结果: OK! Ititatest 显示不换行 #!/bin/sh echo-e"OK!\c"#-e开启转义\c不换行 echo"Itisatest"复制 输出结果: OK!Itisatest echo-n不换行输出 echo-n123 echo456复制 最终输出  123456 而不是 123 456 显示命令执行结果 ec

  • 爬虫小案例:输入电影名称获取资源下载链接

    需求:用户输入喜欢的电影名字,程序即可在电影天堂https://www.ygdy8.com爬取电影所对应的下载链接,并将下载链接打印出来 importrequests frombs4importBeautifulSoup fromurllib.requestimportpathname2url #为躲避反爬机制,伪装成浏览器的请求头 headers={'User-Agent':'Mozilla/5.0(Macintosh;IntelMacOSX10_14_3)AppleWebKit/537.36(KHTML,likeGecko)Chrome/78.0.3904.108Safari/537.36OPR/65.0.3467.78(EditionBaidu)'} #获取电影磁力链接 defgetMovieDownloadLink(filmlink): res=requests.get(filmlink,headers=headers) ifres.status_code==200: #请求后的内容中文乱码处理办法: #当response编码是‘ISO-8859-1’,我们应该首先查找

  • 64位系统下System32文件系统重定向

    前言       因为一次偶然的机会,需要访问系统目录“C:/Windows/System32“文件夹下的内容,使用的测试机器上预装了win764系统。在程序运行中竟然发生了该文件路径不存在的问题!!通过查看网上相关的资料,了解到64位系统下,System32(同时也包括ProgramFiles)这两个文件夹被动态地重定向了。为了可以直观的反映这个问题,这里将编写一个小的测试程序进行验证。   实例验证       首先随机选择一个文件,并将其拷贝到系统目录的System32文件夹下。本文选择QQ启动程序进行验证(主要是QQ自带企鹅图标易于辨认,哈哈),如图1所示 图1手动将QQ拷贝到系统System32文件夹下    编写实际测试程序,直接上代码(调用了windows系统APIPathFileExists来判断文件是否存在) /***********************************************

  • 2018.06-2020.05(哈啰出行)

    2018.06-2020.05(哈啰出行) 标签(空格分隔):成长之路 Hadoop体系变更史 HDFS: 基础性能优化 标准化统一机型 各个组件gc优化 配置中心统一化 用户使用标准模型 权限体系建设打通 成本优化 HDFS组件源码优化 Yarn: MapReduce基础调优 队列划分 Yarn架构调整 调度器优化 ResourceManager,NodeManager优化 Hive metastore,HiveServer2稳定性,性能优化 Hive权限体系 Hive存储成本优化,存储格式性能优化 Hive数仓体系建设 Hive元数据优化。 Presto 队列划分 集群拆分 Prestobug修复(参数,源码) Presto权限体系与Hive打通 PrestoWorker本地性能优化 Presto稳定性建设 Ranger Rangeradmin稳定性 RangerAdmin性能优化 RangerPlugin策略构建优化 RangerPlugin惰性加载 Ranger高可用 Spark SparkSQL: SparkthriftServer: PySpark

  • 新的开始之Win7、CentOS 6.4 双系统 硬盘安装

    转载:http://blog.csdn.net/cnclenovo/article/details/11358447 目的: 在已经有Win7的操作系统上安装CentOS6.4的32位操作系统。 本博客结合了以下的博客 http://blog.csdn.net/markho365/article/details/8969591 http://www.cnblogs.com/Johness/archive/2012/12/03/2800126.html 1、安装背景 安装CentOS之前,本机已经安装了Win7。 硬盘的所有空间都被占满了。 安装之前的磁盘状态如下: 注意:我的分区是C盘前面没有任何的分区和空闲空间,而且是一个主分区,其他分区都在逻辑分区内。 提前看看安装后期磁盘状态: 注意:安装完后,我临时存放镜像及安装所需的其他文件的H盘在我安装CentOS的所有盘的前面,注意这个顺序,千万不要错了。(我血的教训) 2、使用到的工具 centos/6/isos/i386/ CentOS-6.4-64-i386-bin-dvd 最好使用dvd版的,分为两个dvd,虽然大,

  • hdu 5755 2016 Multi-University Training Contest 3 Gambler Bo 高斯消元模3同余方程

    http://acm.hdu.edu.cn/showproblem.php?pid=5755 题意:一个N*M的矩阵,改变一个格子,本身+2,四周+1.同时mod3;问操作多少次,矩阵变为全0.输出次数和具体位置 由于影响是相互的,所以增广矩阵的系数a[t][t+1]或者是a[t+1][t]均可;只需注意往结果中添加位置时,x[i]表示要操作x[i]次,所以位置要重复添加x[i]次; 高斯消元时间复杂度为O(N3M3); 1#pragmacomment(linker,"/STACK:1024000000,1024000000") 2#include<bits/stdc++.h> 3usingnamespacestd; 4#definerep0(i,l,r)for(inti=(l);i<(r);i++) 5#definerep1(i,l,r)for(inti=(l);i<=(r);i++) 6#definerep_0(i,r,l)for(inti=(r);i>(l);i--) 7#definerep_1(i,r,l)for(inti=(r);i>=(

  • MFC笔记

    1、CString与std::string的相互转换: 1.1:stringtoCString stringstr="helloworld"; CStringcstr(str.c_str()); 复制 1.2:CStringtostring CStringtheCStr=L"HelloC++"; std::stringSTDStr(CW2A(theCStr.GetString())); 复制 2、打开文件选择对话框: voidCpdf2pic3Dlg::OnBnClickedButton2() { //TODO:在此添加控件通知处理程序代码 CStringopenfile=_T("pdf文件(.pdf)|*.pdf|"); CFileDialogfileOpen(TRUE,NULL,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,openfile); if(IDOK==fileOpen.DoModal()){ CStringpath=fileOpen.GetPathName(); //CStringfilename=fileOpen.

  • mysql笔记

    过去初学mysql的笔记,在此存一份在线文档 文章目录 一条sql的执行过程修改密码重置密码修改了权限不生效,需要刷新权限mysqld启动文件修改端口show命令my.cnfmysql服务启不来,要注意这几点:因为sock文件找不到登录不进去,解决办法$datadir目录下的文件解释mysqllogmysqladmin免密登录 一条sql的执行过程 连接数据库(如python的MySQLdb程序代码、api、客户端),创建起来通道连接,在数据库看来是一个空闲状态,创建好通道就可以向数据库服务器发送sql语句,不同的sql语句进入不同的接口(比如查询数据到一个接口,存储过程的sql就到另一个接口),然后开始解析(sql、权限),解析完了开始优化sql怎么去执行(比如是否使用索引、使用哪个索引),执行的时候会校验sql要查询的数据有没有缓存,因为mysql服务本身会占用大量的内存,会检查下内存中是否已经有数据,如果有缓存就返回来,如果没有缓存,就按引擎的规则来查询数据,查到后引擎把数据返回来。如果是做更新也是在引擎做数据结构的变更,变更后再返回结果。引擎决定数据怎么存储、怎么

  • Js/Jquery获取iframe中的元素 在Iframe中获取父窗体的元素方法

    在web开发中,经常会用到iframe,难免会碰到需要在父窗口中使用iframe中的元素、或者在iframe框架中使用父窗口的元素 js 在父窗口中获取iframe中的元素  1、 格式:window.frames["iframe的name值"].document.getElementByIdx_x("iframe中控件的ID").click(); 实例:window.frames["ifm"].document.getElementByIdx_x("btnOk").click(); 2、 格式: varobj=document.getElementByIdx_x("iframe的name").contentWindow; varifmObj=obj.document.getElementByIdx_x("iframe中控件的ID"); ifmObj.click(); 实例: varobj=document.getElementByIdx_x("ifm").contentWindow; varifmObj=obj.document.getElementByIdx_x("btn

  • pyenv,轻松切换各种python版本

    pyenv,轻松切换各种python版本 解决什么问题 mac自带python2,md又不能删掉他 linux也自带python2,这玩意都过时了,也不赶紧换掉 安装pyenv git安装 gitclonehttps://github.com/pyenv/pyenv.git~/.pyenv 复制 bash环境,就依次执行如下命令: echo'exportPYENV_ROOT="$HOME/.pyenv"'>>~/.bashrc echo'exportPATH="$PYENV_ROOT/bin:$PATH"'>>~/.bashrc echo-e'ifcommand-vpyenv1>/dev/null2>&1;then\neval"$(pyenvinit-)"\nfi'>>~/.bashrc 复制 zsh 环境,就依次执行如下命令: echo'exportPYENV_ROOT="$HOME/.pyenv"'>>~/.zshrc echo'exportPATH="$PYENV_ROOT/bin:$PATH"'&

相关推荐

推荐阅读