关于flink:生产实践-基于-Flink-的短视频生产消费监控

49次阅读

共计 39821 个字符,预计需要花费 100 分钟才能阅读完成。

<section id=”nice” data-tool=”mdnice 编辑器 ” data-website=”https://www.mdnice.com” style=”color: black; line-height: 1.6; word-spacing: 0px; letter-spacing: 0px; word-break: break-word; word-wrap: break-word; text-align: left; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, ‘PingFang SC’, Cambria, Cochin, Georgia, Times, ‘Times New Roman’, serif; font-size: 14px; padding: 10px;”>
<blockquote data-tool=”mdnice 编辑器 ” style=”display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; color: #6a737d; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px; border-left: 3px solid rgba(0, 0, 0, 0.65); border-right: 1px solid rgba(0, 0, 0, 0.65); background: rgb(249, 249, 249);”>
<p style=”padding-top: 8px; padding-bottom: 8px; font-size: 14px; margin: 0px; color: black; line-height: 26px;”> 本文具体介绍了实时监控类指标的数据流转链路以及技术计划,大多数的实时监控类指标都可依照本文中的几种计划实现。</p>
</blockquote>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; padding: 0px; font-size: 22px; text-align: center; position: relative; font-weight: bold; color: black; line-height: 1.1em; padding-top: 12px; padding-bottom: 12px; margin: 70px 30px 30px; border: 1px solid #000;”><span style=”float: left; display: block; width: 90%; border-top: 1px solid #000; height: 1px; line-height: 1px; margin-left: -5px; margin-top: -17px;”> </span><span class=”prefix” style=”display: block; width: 3px; margin: 0 0 0 5%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,

0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span class="content"style="display: block; -webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"> 短视频生产生产监控 </span><span class="suffix"style="display: block; width: 3px; margin: 0 0 0 95%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,
0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span style="float: right; display: block; width: 90%; border-bottom: 1px solid #000; height: 1px; line-height: 1px; margin-right: -5px; margin-top: 16px;"> </span></h2>

<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 短视频带来了全新的流传场域和节目状态,小屏幕、快节奏成为行业潮流的同时,也催生了新的用户生产习惯,为创作者和商户带来收益。而多元化的短视频也能够为品牌方提供营销时机。</p>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 其中对于垂类生态短视频的生产生产热点的监控剖析目前成为了实时数据处理很常见的一个利用场景,比方对某个圈定的垂类生态下的视频生产或者视频生产进行监控,对热点视频生成对应的优化举荐策略,促成热点视频的生产或者生产,构建整个生产生产数据链路的闭环,从而进步创作者收益以及消费者留存。</p>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 本文将残缺剖析垂类生态短视频生产生产数据的整条链路流转形式,并基于 Flink 提供几种对于垂类视频生产生产监控的方案设计。通过本文,你能够理解到:</p>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: square;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”><p style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”>垂类生态短视频生产生产数据链路闭环 </p>
</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”><p style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 实时监控短视频生产生产的方案设计 </p>
</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”><p style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 不同监控量级场景下的代码实现</p>
</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”><p style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”>flink 学习材料</p>
</section></li></ul>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; padding: 0px; font-size: 22px; text-align: center; position: relative; font-weight: bold; color: black; line-height: 1.1em; padding-top: 12px; padding-bottom: 12px; margin: 70px 30px 30px; border: 1px solid #000;”><span style=”float: left; display: block; width: 90%; border-top: 1px solid #000; height: 1px; line-height: 1px; margin-left: -5px; margin-top: -17px;”> </span><span class=”prefix” style=”display: block; width: 3px; margin: 0 0 0 5%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,

0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span class="content"style="display: block; -webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"> 我的项目简介 </span><span class="suffix"style="display: block; width: 3px; margin: 0 0 0 95%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,
0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span style="float: right; display: block; width: 90%; border-bottom: 1px solid #000; height: 1px; line-height: 1px; margin-right: -5px; margin-top: 16px;"> </span></h2>

<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 垂类生态短视频生产生产数据链路流转架构图如下,此数据流转图也实用于其余场景:</p>
<figure data-tool=”mdnice 编辑器 ” style=”margin: 0; margin-top: 10px; margin-bottom: 10px; display: flex; flex-direction: column; justify-content: center; align-items: center;”><img src=”https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8506ceb76f1a4a09b5b04f52b2706f31~tplv-k3u1fbpfcp-zoom-1.image” alt=” 链路 ” style=”display: block; margin: 0 auto; max-width: 100%; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; border-radius: 4px; margin-top: 10px;”><figcaption style=”margin-top: 5px; text-align: center; color: #888; font-size: 12px;”> 链路 </figcaption></figure>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 在上述场景中,用户生产和生产短视频,从而客户端、服务端以及数据库会产生相应的行为操作日志,这些日志会通过日志抽取中间件抽取到音讯队列中,咱们目前的场景中是应用 Kafka 作为音讯队列;而后应用 flink 对垂类生态中的视频进行生产或生产监控(内容生产通常是圈定垂类作者 id 池,内容生产通常是圈定垂类视频 id 池),最初将实时聚合数据产出到上游;上游能够以数据服务,实时看板的形式展示,经营同学或者自动化工具最终会帮忙咱们剖析以后垂类下的生产或者生产热点,从而生成举荐策略。</p>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; padding: 0px; font-size: 22px; text-align: center; position: relative; font-weight: bold; color: black; line-height: 1.1em; padding-top: 12px; padding-bottom: 12px; margin: 70px 30px 30px; border: 1px solid #000;”><span style=”float: left; display: block; width: 90%; border-top: 1px solid #000; height: 1px; line-height: 1px; margin-left: -5px; margin-top: -17px;”> </span><span class=”prefix” style=”display: block; width: 3px; margin: 0 0 0 5%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,

0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span class="content"style="display: block; -webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"> 方案设计 </span><span class="suffix"style="display: block; width: 3px; margin: 0 0 0 95%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,
0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span style="float: right; display: block; width: 90%; border-bottom: 1px solid #000; height: 1px; line-height: 1px; margin-right: -5px; margin-top: 16px;"> </span></h2>

<figure data-tool=”mdnice 编辑器 ” style=”margin: 0; margin-top: 10px; margin-bottom: 10px; display: flex; flex-direction: column; justify-content: center; align-items: center;”><img src=”https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c4102da8091547f2910f8bf5acb29b46~tplv-k3u1fbpfcp-zoom-1.image” alt=” 架构 ” style=”display: block; margin: 0 auto; max-width: 100%; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; border-radius: 4px; margin-top: 10px;”><figcaption style=”margin-top: 5px; text-align: center; color: #888; font-size: 12px;”> 架构 </figcaption></figure>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 其中数据源如下:</p>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: square;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”>Kafka 为全量内容生产和内容生产的日志。</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”>Rpc/Http/Mysql/ 配置核心 /Redis/HBase 为须要监控的垂类生态内容 id 池(内容生产则为作者 id 池,内容生产则为视频 id 池),其次要是提供给经营同学动静配置须要监控的 id 范畴,其能够在 flink 中进行实时查问,解析经营同学想要的监控指标范畴,以及监控的指标和计算形式,而后加工数据产出,能够反对随时配置,实时数据随时计算产出。</section></li></ul>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 其中数据汇为聚类好的内容生产或者生产热点话题或者事件指标:</p>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: square;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”>Redis/HBase 次要是以低提早(Redis 5ms p99,HBase 100ms p99,不同公司的服务能力不同)并且高 QPS 提供数据服务,给 Server 端或者线上用户提供低提早的数据查问。</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”>Druid/Mysql 能够做为 OLAP 引擎为 BI 剖析提供灵便的上卷下钻聚合剖析能力,供经营同学配置可视化图表应用。</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”>Kafka 能够以流式数据产出,从而提供给上游持续生产或者进行特征提取。</section></li></ul>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 废话不多说,咱们间接上计划和代码,下述几种计划依照监控 id 范畴量级辨别,不同的量级对应着不同的计划,其中的代码示例为 ProcessWindowFunction,也能够应用 AggregateFunction 代替,其中次要监控逻辑都雷同。</p>
<h3 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; font-weight: bold; background-color: #000; color: #fff; padding: 2px 10px; width: fit-content; font-size: 17px; margin: 60px auto 10px;”><span class=”prefix” style=”display: none;”></span><span class=”content”> 计划 1</span><span class=”suffix” style=”display: none;”></span></h3>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 适宜监控 id 数据量小的场景(几千 id),其实现形式是在 flink 工作初始化时将须要监控的 id 池或动静配置核心的 id 池加载到内存当中,之后只须要在内存中判断内容生产或者生产数据是否在这个监控池当中。</p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; max-width: 100%; border-radius: 4px; margin: 10px auto 0 auto;”><span style=”display: block; background: url(https://my-wechat.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;”></span>ProcessWindowFunction p = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> ProcessWindowFunction<CommonModel, CommonModel, Long, TimeWindow>() {
<span/>
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 配置核心动静 id 池 </span>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> Config<Set<Long>> needMonitoredIdsConfig;
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">open</span><span class="hljs-params" style="line-height: 26px;">(Configuration parameters)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.needMonitoredIdsConfig = ConfigBuilder
<span/> .buildSet(<span class="hljs-string" style="color: #98c379; line-height: 26px;">"needMonitoredIds"</span>, Long.class);
<span/> }
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">process</span><span class="hljs-params" style="line-height: 26px;">(Long bucket, Context context, Iterable<CommonModel> iterable, Collector<CommonModel> collector)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> Set<Long> needMonitoredIds = needMonitoredIdsConfig.get();
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**
<span/> * 判断 commonModel 中的 id 是否在 needMonitoredIds 池中
<span/> */</span>
<span/> }
<span/>}
<span/>
</pre>
<blockquote data-tool=”mdnice 编辑器 ” style=”display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; color: #6a737d; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px; border-left: 3px solid rgba(0, 0, 0, 0.65); border-right: 1px solid rgba(0, 0, 0, 0.65); background: rgb(249, 249, 249);”>
<p style=”padding-top: 8px; padding-bottom: 8px; font-size: 14px; margin: 0px; color: black; line-height: 26px;”> 监控的 id 池能够依照固定或者可配置从而分出两种获取形式:第一种是在 flink 工作开始时就全副加载进内存中,这种形式适宜监控 id 池不变的状况;第二种是应用动静配置核心,每次都从配置核心拜访到最新的监控 id 池,其能够满足动静配置或者更改 id 池的需要,并且这种实现形式通常能够实时感知到配置更改,简直无提早。</p>
</blockquote>
<h3 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; font-weight: bold; background-color: #000; color: #fff; padding: 2px 10px; width: fit-content; font-size: 17px; margin: 60px auto 10px;”><span class=”prefix” style=”display: none;”></span><span class=”content”> 计划 2</span><span class=”suffix” style=”display: none;”></span></h3>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 适宜监控 id 数据量适中( 几十万 id),监控数据范畴会不定时产生变动的场景。其实现形式是在 flink 算子中定时拜访接口获取最新的监控 id 池,以获取最新监控数据范畴。</p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; max-width: 100%; border-radius: 4px; margin: 10px auto 0 auto;”><span style=”display: block; background: url(https://my-wechat.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;”></span>ProcessWindowFunction p = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> ProcessWindowFunction<CommonModel, CommonModel, Long, TimeWindow>() {
<span/>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">long</span> lastRefreshTimestamp;
<span/>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> Set<Long> needMonitoredIds;
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">open</span><span class="hljs-params" style="line-height: 26px;">(Configuration parameters)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">super</span>.open(parameters);
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.refreshNeedMonitoredIds(System.currentTimeMillis());
<span/> }
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">process</span><span class="hljs-params" style="line-height: 26px;">(Long bucket, Context context, Iterable<CommonModel> iterable, Collector<CommonModel> collector)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">long</span> windowStart = context.window().getStart();
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.refreshNeedMonitoredIds(windowStart);
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**
<span/> * 判断 commonModel 中的 id 是否在 needMonitoredIds 池中
<span/> */</span>
<span/> }
<span/>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">refreshNeedMonitoredIds</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">long</span> windowStart)</span> </span>{
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 每隔 10 秒拜访一次 </span>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (windowStart - <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.lastRefreshTimestamp >= <span class="hljs-number" style="color: #d19a66; line-height: 26px;">10000L</span>) {
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.lastRefreshTimestamp = windowStart;
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.needMonitoredIds = Rpc.get(...)
<span/> }
<span/> }
<span/>}
<span/>
</pre>
<blockquote data-tool=”mdnice 编辑器 ” style=”display: block; font-size: 0.9em; overflow: auto; overflow-scrolling: touch; color: #6a737d; padding-top: 10px; padding-bottom: 10px; padding-left: 20px; padding-right: 10px; margin-bottom: 20px; margin-top: 20px; border-left: 3px solid rgba(0, 0, 0, 0.65); border-right: 1px solid rgba(0, 0, 0, 0.65); background: rgb(249, 249, 249);”>
<p style=”padding-top: 8px; padding-bottom: 8px; font-size: 14px; margin: 0px; color: black; line-height: 26px;”> 根据上述代码实现形式,依照工夫距离的形式刷新 id 池,其毛病在于不能实时感知监控 id 池的变动,所以刷新工夫可能会和需要场景强耦合(如果 id 池会频繁更新,那么就须要放大刷新工夫距离)。也可依据需要场景在每个窗口开始前刷新 id 池,这样可保障每个窗口中的 id 池中的数据始终放弃更新。</p>
</blockquote>
<h3 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; font-weight: bold; background-color: #000; color: #fff; padding: 2px 10px; width: fit-content; font-size: 17px; margin: 60px auto 10px;”><span class=”prefix” style=”display: none;”></span><span class=”content”> 计划 3</span><span class=”suffix” style=”display: none;”></span></h3>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 计划 3 对计划 2 的一个优化( 几十万 id,咱们生产环境中最罕用的 )。其实现形式是在 flink 中应用 broadcast 算子定时拜访监控 id 池,并将 id 池以播送的模式下发给上游参加计算的各个算子。其优化点在于:比方工作的并行度为 500,每 1s 拜访一次,采纳计划 2 则拜访监控 id 池接口的 QPS 为 500,在应用 broadcast 算子之后,其拜访 QPS 能够缩小到 1,能够大大减少对接口的访问量,加重接口压力。</p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; max-width: 100%; border-radius: 4px; margin: 10px auto 0 auto;”><span style=”display: block; background: url(https://my-wechat.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;”></span><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">class</span> <span class="hljs-title" style="color: #e6c07b; line-height: 26px;">Example</span> </span>{
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Slf</span>4j
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">static</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">class</span> <span class="hljs-title" style="color: #e6c07b; line-height: 26px;">NeedMonitorIdsSource</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">implements</span> <span class="hljs-title" style="color: #e6c07b; line-height: 26px;">SourceFunction</span><<span class="hljs-title" style="color: #e6c07b; line-height: 26px;">Map</span><<span class="hljs-title" style="color: #e6c07b; line-height: 26px;">Long</span>, <span class="hljs-title" style="color: #e6c07b; line-height: 26px;">Set</span><<span class="hljs-title" style="color: #e6c07b; line-height: 26px;">Long</span>>>> </span>{
<span/>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">volatile</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">boolean</span> isCancel;
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">run</span><span class="hljs-params" style="line-height: 26px;">(SourceContext<Map<Long, Set<Long>>> sourceContext)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">while</span> (!<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.isCancel) {
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">try</span> {
<span/> TimeUnit.SECONDS.sleep(<span class="hljs-number" style="color: #d19a66; line-height: 26px;">1</span>);
<span/> Set<Long> needMonitorIds = Rpc.get(...);
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 能够和上一次拜访的数据做比拟查看是否有变动,如果有变动,才发送进来 </span>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (CollectionUtils.isNotEmpty(needMonitorIds)) {
<span/> sourceContext.collect(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> HashMap<Long, Set<Long>>() {{
<span/> put(<span class="hljs-number">0L</span>, needMonitorIds);
<span/> }});
<span/> }
<span/> } <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">catch</span> (Throwable e) {
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 避免接口拜访失败导致的谬误导致 flink job 挂掉 </span>
<span/> log.error(<span class="hljs-string" style="color: #98c379; line-height: 26px;">"need monitor ids error"</span>, e);
<span/> }
<span/> }
<span/> }
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">cancel</span><span class="hljs-params" style="line-height: 26px;">()</span> </span>{
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.isCancel = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">true</span>;
<span/> }
<span/> }
<span/>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{
<span/> ParameterTool parameterTool = ParameterTool.fromArgs(args);
<span/> InputParams inputParams = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> InputParams(parameterTool);
<span/> StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment();
<span/>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> MapStateDescriptor<Long, Set<Long>> broadcastMapStateDescriptor = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> MapStateDescriptor<>(
<span/> <span class="hljs-string" style="color: #98c379; line-height: 26px;">"config-keywords"</span>,
<span/> BasicTypeInfo.LONG_TYPE_INFO,
<span/> TypeInformation.of(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> TypeHint<Set<Long>>() {
<span/> }));
<span/>
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/ kafka source /</span>
<span/> BroadcastStream<Map<Long, Set<Long>>> broadcastStream = env
<span/> .addSource(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> NeedMonitorIdsSource()) <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// redis photoId 数据播送 </span>
<span/> .setParallelism(<span class="hljs-number" style="color: #d19a66; line-height: 26px;">1</span>)
<span/> .broadcast(broadcastMapStateDescriptor);
<span/>
<span/> DataStream<CommonModel> logSourceDataStream = SourceFactory.getSourceDataStream(...);
<span/>
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/ dag /</span>
<span/> DataStream<CommonModel> resultDataStream = logSourceDataStream
<span/> .keyBy(KeySelectorFactory.getStringKeySelector(CommonModel::getKeyField))
<span/> .connect(broadcastStream)
<span/> .process(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> KeyedBroadcastProcessFunction<String, CommonModel, Map<Long, Set<Long>>, CommonModel>() {
<span/>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> Set<Long> needMonitoredIds;
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">open</span><span class="hljs-params" style="line-height: 26px;">(Configuration parameters)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">super</span>.open(parameters);
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.needMonitoredIds = Rpc.get(...)
<span/> }
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">processElement</span><span class="hljs-params" style="line-height: 26px;">(CommonModel commonModel, ReadOnlyContext readOnlyContext, Collector<CommonModel> collector)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 判断 commonModel 中的 id 是否在 needMonitoredIds 池中 </span>
<span/> }
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">processBroadcastElement</span><span class="hljs-params" style="line-height: 26px;">(Map<Long, Set<Long>> longSetMap, Context context, Collector<CommonModel> collector)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 须要监控的字段 </span>
<span/> Set<Long> needMonitorIds = longSetMap.get(<span class="hljs-number" style="color: #d19a66; line-height: 26px;">0L</span>);
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (CollectionUtils.isNotEmpty(needMonitorIds)) {
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.needMonitoredIds = needMonitorIds;
<span/> }
<span/> }
<span/> });
<span/>
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/ kafka sink /</span>
<span/> SinkFactory.setSinkDataStream(...);
<span/>
<span/> env.execute(inputParams.jobName);
<span/> }
<span/>
<span/>}
<span/>
</pre>
<h3 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; font-weight: bold; background-color: #000; color: #fff; padding: 2px 10px; width: fit-content; font-size: 17px; margin: 60px auto 10px;”><span class=”prefix” style=”display: none;”></span><span class=”content”> 计划 4</span><span class=”suffix” style=”display: none;”></span></h3>
<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 适宜于超大监控范畴的数据( 几百万,咱们本人的生产实践中应用扩量到 500 万)。其原理是将监控范畴接口依照 id 依照肯定规定分桶。flink 生产到日志数据后将 id 依照 监控范畴接口 id 雷同的分桶办法进行分桶 keyBy,这样在上游算子中每个算子中就能够依照桶名称,从接口中拿到对应桶的监控 id 数据,这样 flink 中并行的每个算子只须要获取到本人对应的桶的数据,能够大大减少申请的压力。</p>
<pre class=”custom” data-tool=”mdnice 编辑器 ” style=”margin-top: 10px; margin-bottom: 10px; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; max-width: 100%; border-radius: 4px; margin: 10px auto 0 auto;”><span style=”display: block; background: url(https://my-wechat.mdnice.com/point.png); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;”></span><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-class" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">class</span> <span class="hljs-title" style="color: #e6c07b; line-height: 26px;">Example</span> </span>{
<span/>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">static</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">main</span><span class="hljs-params" style="line-height: 26px;">(String[] args)</span> </span>{
<span/> ParameterTool parameterTool = ParameterTool.fromArgs(args);
<span/> InputParams inputParams = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> InputParams(parameterTool);
<span/> StreamExecutionEnvironment env = StreamExecutionEnvironment.createLocalEnvironment();
<span/>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">final</span> MapStateDescriptor<Long, Set<Long>> broadcastMapStateDescriptor = <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> MapStateDescriptor<>(
<span/> <span class="hljs-string" style="color: #98c379; line-height: 26px;">"config-keywords"</span>,
<span/> BasicTypeInfo.LONG_TYPE_INFO,
<span/> TypeInformation.of(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> TypeHint<Set<Long>>() {
<span/> }));
<span/>
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/ kafka source /</span>
<span/>
<span/> DataStream<CommonModel> logSourceDataStream = SourceFactory.getSourceDataStream(...);
<span/>
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/ dag /</span>
<span/> DataStream<CommonModel> resultDataStream = logSourceDataStream
<span/> .keyBy(KeySelectorFactory.getLongKeySelector(CommonModel::getKeyField))
<span/> .timeWindow(Time.seconds(inputParams.accTimeWindowSeconds))
<span/> .process(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">new</span> ProcessWindowFunction<CommonModel, CommonModel, Long, TimeWindow>() {
<span/>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">long</span> lastRefreshTimestamp;
<span/>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">private</span> Set<Long> oneBucketNeedMonitoredIds;
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">open</span><span class="hljs-params" style="line-height: 26px;">(Configuration parameters)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">super</span>.open(parameters);
<span/> }
<span/>
<span/> <span class="hljs-meta" style="color: #61aeee; line-height: 26px;">@Override</span>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">process</span><span class="hljs-params" style="line-height: 26px;">(Long bucket, Context context, Iterable<CommonModel> iterable, Collector<CommonModel> collector)</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">throws</span> Exception </span>{
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">long</span> windowStart = context.window().getStart();
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.refreshNeedMonitoredIds(windowStart, bucket);
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/**
<span/> * 判断 commonModel 中的 id 是否在 needMonitoredIds 池中
<span/> */</span>
<span/> }
<span/>
<span/> <span class="hljs-function" style="line-height: 26px;"><span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">public</span> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">void</span> <span class="hljs-title" style="color: #61aeee; line-height: 26px;">refreshNeedMonitoredIds</span><span class="hljs-params" style="line-height: 26px;">(<span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">long</span> windowStart, <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">long</span> bucket)</span> </span>{
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">// 每隔 10 秒拜访一次 </span>
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">if</span> (windowStart - <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.lastRefreshTimestamp >= <span class="hljs-number" style="color: #d19a66; line-height: 26px;">10000L</span>) {
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.lastRefreshTimestamp = windowStart;
<span/> <span class="hljs-keyword" style="color: #c678dd; line-height: 26px;">this</span>.oneBucketNeedMonitoredIds = Rpc.get(bucket, ...)
<span/> }
<span/> }
<span/> });
<span/>
<span/> <span class="hljs-comment" style="color: #5c6370; font-style: italic; line-height: 26px;">/ kafka sink /</span>
<span/> SinkFactory.setSinkDataStream(...);
<span/>
<span/> env.execute(inputParams.jobName);
<span/> }
<span/>}
<span/>
</pre>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; padding: 0px; font-size: 22px; text-align: center; position: relative; font-weight: bold; color: black; line-height: 1.1em; padding-top: 12px; padding-bottom: 12px; margin: 70px 30px 30px; border: 1px solid #000;”><span style=”float: left; display: block; width: 90%; border-top: 1px solid #000; height: 1px; line-height: 1px; margin-left: -5px; margin-top: -17px;”> </span><span class=”prefix” style=”display: block; width: 3px; margin: 0 0 0 5%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,

0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span class="content"style="display: block; -webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"> 总结 </span><span class="suffix"style="display: block; width: 3px; margin: 0 0 0 95%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,
0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span style="float: right; display: block; width: 90%; border-bottom: 1px solid #000; height: 1px; line-height: 1px; margin-right: -5px; margin-top: 16px;"> </span></h2>

<p data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 本文首先介绍了,在短视频畛域中,短视频生产生产数据链路的整个闭环,并且其数据链路闭环个别状况下也实用于其余场景;以及对应的实时监控计划的设计和不同场景下的代码实现,包含:</p>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: square;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”><p style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”>垂类生态短视频生产生产数据链路闭环:用户操作行为日志的流转,日志上传,实时计算,以及流转到 BI,数据服务,最初数据赋能的整个流程 </p>
</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”><p style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 实时监控方案设计:监控类实时计算流程中各类数据源,数据汇的选型 </p>
</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”><p style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px;”> 监控 id 池在不同量级场景下具体代码实现</p>
</section></li></ul>
<h2 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; padding: 0px; font-size: 22px; text-align: center; position: relative; font-weight: bold; color: black; line-height: 1.1em; padding-top: 12px; padding-bottom: 12px; margin: 70px 30px 30px; border: 1px solid #000;”><span style=”float: left; display: block; width: 90%; border-top: 1px solid #000; height: 1px; line-height: 1px; margin-left: -5px; margin-top: -17px;”> </span><span class=”prefix” style=”display: block; width: 3px; margin: 0 0 0 5%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,

0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span class="content"style="display: block; -webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"> 学习材料 </span><span class="suffix"style="display: block; width: 3px; margin: 0 0 0 95%; height: 3px; line-height: 3px; overflow: hidden; background-color: #000; box-shadow: 3px 0 #000,
0 3px #000,
-3px 0 #000,
0 -3px #000;"></span><span style="float: right; display: block; width: 90%; border-bottom: 1px solid #000; height: 1px; line-height: 1px; margin-right: -5px; margin-top: 16px;"> </span></h2>

<h3 data-tool=”mdnice 编辑器 ” style=”margin-top: 30px; margin-bottom: 15px; font-weight: bold; background-color: #000; color: #fff; padding: 2px 10px; width: fit-content; font-size: 17px; margin: 60px auto 10px;”><span class=”prefix” style=”display: none;”></span><span class=”content”>flink</span><span class=”suffix” style=”display: none;”></span></h3>
<ul data-tool=”mdnice 编辑器 ” style=”margin-top: 8px; margin-bottom: 8px; padding-left: 25px; color: black; list-style-type: square;”>
<li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”>https://github.com/flink-chin…</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”>https://ververica.cn/develope…</section></li><li><section style=”margin-top: 5px; margin-bottom: 5px; line-height: 26px; text-align: left; color: rgb(1,1,1); font-weight: 500;”>https://space.bilibili.com/33…</section></li></ul>
<p id=”nice-suffix-juejin-container” class=”nice-suffix-juejin-container” data-tool=”mdnice 编辑器 ” style=”padding-top: 8px; padding-bottom: 8px; margin: 0; line-height: 26px; color: black; font-size: 14px; margin-top: 20px !important;”>
<img src=”https://upload-images.jianshu.io/upload_images/11390349-24f3675a115b905a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240″ alt=” 关注 ” style=”display: block; margin: 0 auto; max-width: 100%; box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px; border-radius: 4px; margin-top: 10px;”>

正文完
 0