研发背景

公司安全部目前针对内部系统的网络访问日志的安全审计,大部分都是T+1时效,每日当天,启动Python编写的定时任务,完成昨日的日志审计和检测,定时任务运行完成后,统一进行企业微信告警推送。这种方案在目前的网络环境和人员规模下,呈现两个痛点,一是面对日益频繁的网络攻击、钓鱼链接,T+1的定时任务,难以及时进行告警,因此也难以有效避免如关键信息泄露等问题,二是目前以Python为主的单机定时任务,针对不同场景的处理时效,从一小时到十几小时不等,效率低下。为解决以上问题,本人协助公司安全部同时对告警采集平台进行改造,由之前的python单机任务处理,切换到基于Flink集群的并行处理,且告警推送时效,由之前的T+1天,提升到秒级实时告警。本次改造涉及网络日志审计的多个常见场景,如端口扫描、黑名单统计、异常流量、连续恶意登录等。本次以一段时间内连续登录失败20次后,下一次登录成功场景来进行介绍。

场景描述

针对一个内部系统,如邮件系统,公司员工的访问行为日志,存放于kafka,我们希望对于一个用户账号在同一个IP下,任意的3分钟时间内,连续登录邮件系统20次失败,下一次登录成功,这种场景能够及时获取并推送到企业微信某个指定的安全接口人。kafka中的数据,能够通过某个关键字,区分当前网络访问是否一次登录事件,且有访问时间(也就是事件时间)。在解析到符合需求的用户账号之后,第一时间进行企业微信告警推送,并将其这段时间内的访问行为,写入下游ElasticSearch。

组件版本

  • Flink-1.14.4
  • Java8
  • ElasticSearch-7.3.2
  • Kafka-2.12_2.8.1

日志结构

IP和账号皆为测试使用。

{
"user": "wangxm",
"client_ip": "110.68.6.182",
"source": "login",
"loginname": "wangxm@test.com",
"IP": "110.8.148.58",
"timestamp": "17:58:12",
"@timestamp": "2022-04-20T09:58:13.647Z",
"ip": "110.7.231.25",
"clienttype": "POP3",
"result": "success",
"@version": "1"
}

技术方案

上述场景,可考虑使用FlinkCEP及Flink的滑动窗口进行实现。由于本人在采用FlinkCEP的方案进行代码编写调试后,发现并不能满足,因此改用滑动窗口进行实现。

关键代码

主入口类

主入口类,创建了flink环境、设置了基础参数,创建了kafkaSource,接入消息后,进行了映射、过滤,并设置了水位线,进行了分组,之后设置了滑动窗口,在窗口内进行了事件统计,将复合条件的事件收集返回并写入ElasticSearch。

针对map、filter、keyBy、window等算子,都单独进行了编写,后面会一一列出来。

package com.data.dev.flink.mailTopic.main;

import com.data.dev.common.javabean.BaseBean;
import com.data.dev.common.javabean.kafkaMailTopic.MailMsgAlarm;
import com.data.dev.elasticsearch.ElasticSearchInfo;
import com.data.dev.elasticsearch.SinkToEs;
import com.data.dev.flink.FlinkEnv;
import com.data.dev.flink.mailTopic.OperationForLoginFailCheck.*;
import com.data.dev.kafka.KafkaSourceBuilder;
import com.data.dev.key.ConfigurationKey;
import com.data.dev.utils.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.api.common.eventtime.WatermarkStrategy;
import org.apache.flink.connector.kafka.source.KafkaSource;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.KeyedStream;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.datastream.WindowedStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.windowing.assigners.SlidingEventTimeWindows;
import org.apache.flink.streaming.api.windowing.time.Time;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow; import java.time.Duration; /**
* Flink处理在3分钟内连续登录失败20次后登录成功的场景
* 采用滑动窗口来实现
* @author wangxiaomin 2022-06-01
*/ @Slf4j
public class MailMsg extends BaseBean { /**
* Flink作业名称
*/
public static final String JobName = "告警采集平台——连续登录失败后登录成功告警";
/**
* Kafka消息名
*/
public static final String KafkaSourceName = "Kafka Source for AlarmPlatform About Mail Topic"; public MailMsg(){
log.info("初始化滑动窗口场景告警程序");
} /**
* 执行逻辑统计场景,实现告警推送
*/
public static void execute(){ //① 创建Flink执行环境并设置checkpoint等必要的参数
StreamExecutionEnvironment env = FlinkEnv.getFlinkEnv();
KafkaSource<String> kafkaSource = KafkaSourceBuilder.getKafkaSource(ConfigurationKey.KAFKA_MAIL_TOPIC_NAME,ConfigurationKey.KAFKA_MAIL_CONSUMER_GROUP_ID) ;
DataStreamSource<String> kafkaMailMsg = env.fromSource(kafkaSource, WatermarkStrategy.forBoundedOutOfOrderness(Duration.ofMillis(10)), KafkaSourceName); //② 筛选登录消息,创建初始登录事件流
SingleOutputStreamOperator<com.data.dev.common.javabean.kafkaMailTopic.MailMsg> loginMapDs = kafkaMailMsg.map(new MsgToBeanMapper()).name("Map算子加工");
SingleOutputStreamOperator<com.data.dev.common.javabean.kafkaMailTopic.MailMsg> loginFilterDs = loginMapDs.filter(new MailMsgForLoginFilter()).name("Filter算子加工"); //③ 设置水位线
WatermarkStrategy<com.data.dev.common.javabean.kafkaMailTopic.MailMsg> watermarkStrategy = WatermarkStrategy.<com.data.dev.common.javabean.kafkaMailTopic.MailMsg>forBoundedOutOfOrderness(Duration.ofMinutes(1))
.withTimestampAssigner((mailMsg, timestamp) -> TimeUtils.switchUTCToBeijingTimestamp(mailMsg.getTimestamp_datetime()));
SingleOutputStreamOperator<com.data.dev.common.javabean.kafkaMailTopic.MailMsg> loginWmDs = loginFilterDs.assignTimestampsAndWatermarks(watermarkStrategy.withIdleness(Duration.ofMinutes(3))).name("增加水位线"); //④ 设置主键
KeyedStream<com.data.dev.common.javabean.kafkaMailTopic.MailMsg, String> loginKeyedDs = loginWmDs.keyBy(new LoginKeySelector()); //⑥ 转化为滑动窗口
WindowedStream<com.data.dev.common.javabean.kafkaMailTopic.MailMsg, String, TimeWindow> loginWindowDs = loginKeyedDs.window(SlidingEventTimeWindows.of(Time.seconds(180L),Time.seconds(90L))); //⑦ 在窗口内进行逻辑统计
SingleOutputStreamOperator<MailMsgAlarm> loginWindowsDealDs = loginWindowDs.process(new WindowProcessFuncImpl()).name("窗口处理逻辑"); //⑧ 将结果转化为通用DataStream<String>格式
SingleOutputStreamOperator<String> resultDs = loginWindowsDealDs.map(new AlarmMsgToStringMapper()).name("窗口结果转化为标准格式"); //⑨ 将最终结果写入ES
resultDs.addSink(SinkToEs.getEsSinkBuilder(ElasticSearchInfo.ES_LOGIN_FAIL_INDEX_NAME,ElasticSearchInfo.ES_INDEX_TYPE_DEFAULT).build()); //⑩ 提交Flink集群进行执行
FlinkEnv.envExec(env,JobName); }
}

mapper算子

package com.data.dev.flink.mailTopic.OperationForLoginFailCheck;

import com.alibaba.fastjson.JSON;
import com.data.dev.common.javabean.BaseBean;
import com.data.dev.common.javabean.kafkaMailTopic.MailMsgAlarm;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.api.common.functions.MapFunction; /**
* 逻辑统计场景告警推送ES消息体
* @author wangxiaoming-ghq 2022-06-01
*/
@Slf4j
public class AlarmMsgToStringMapper extends BaseBean implements MapFunction<MailMsgAlarm, String> { @Override
public String map(MailMsgAlarm mailMsgAlarm) throws Exception {
return JSON.toJSONString(mailMsgAlarm);
}
}

filter算子

package com.data.dev.flink.mailTopic.OperationForLoginFailCheck;

import com.data.dev.common.javabean.BaseBean;
import com.data.dev.common.javabean.kafkaMailTopic.MailMsg;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.api.common.functions.FilterFunction; /**
* ② 消费mail主题的消息,过滤其中login的事件
* @author wangxiaoming-ghq 2022-06-01
*/
@Slf4j
public class MailMsgForLoginFilter extends BaseBean implements FilterFunction<MailMsg> {
@Override
public boolean filter(MailMsg mailMsg) {
if("login".equals(mailMsg.getSource())) {
log.info("筛选原始的login事件:【" + mailMsg + "】");
}
return "login".equals(mailMsg.getSource());
}
}

keyBy算子

package com.data.dev.flink.mailTopic.OperationForLoginFailCheck;

import com.data.dev.common.javabean.BaseBean;
import com.data.dev.common.javabean.kafkaMailTopic.MailMsg;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.api.java.functions.KeySelector; /**
* CEP 编程,需要进行key选取
*/
@Slf4j
public class LoginKeySelector extends BaseBean implements KeySelector<MailMsg, String> {
@Override
public String getKey(MailMsg mailMsg) {
return mailMsg.getUser() + "@" + mailMsg.getClient_ip();
}
}

窗口函数(核心代码)

package com.data.dev.flink.mailTopic.OperationForLoginFailCheck;

import com.data.dev.common.javabean.kafkaMailTopic.MailMsg;
import com.data.dev.common.javabean.kafkaMailTopic.MailMsgAlarm;
import com.data.dev.utils.HttpUtils;
import com.data.dev.utils.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.streaming.api.functions.windowing.ProcessWindowFunction;
import org.apache.flink.streaming.api.windowing.windows.TimeWindow;
import org.apache.flink.util.Collector; import java.io.Serializable;
import java.util.ArrayList;
import java.util.List; /**
* 滑动窗口内复杂事件解析逻辑实现
* @author wangxiaoming-ghq 2022-06-01
*/
@Slf4j
public class WindowProcessFuncImpl extends ProcessWindowFunction<MailMsg, MailMsgAlarm, String, TimeWindow> implements Serializable {
@Override
public void process(String key, ProcessWindowFunction<MailMsg, MailMsgAlarm, String, TimeWindow>.Context context, Iterable<MailMsg> iterable, Collector<MailMsgAlarm> collector) { List<MailMsg> loginEventList = new ArrayList<>();
MailMsgAlarm mailMsgAlarm;
for (MailMsg mailMsg : iterable) { log.info("收集到的登录事件【" + mailMsg + "】"); if (mailMsg.getResult().equals("fail")) { //开始检测当前窗口内的事件,并将失败的事件收集到loginEventList
log.info("开始检测当前窗口内的事件,并将失败的事件收集到loginEventList");
loginEventList.add(mailMsg);
} else if (mailMsg.getResult().equals("success") && loginEventList.size() < 20) {//如果检测到登录成功事件,但此时登录失败的次数不足20次,则清空loginEventList,等待下一次检测
log.info("检测到登录成功事件,但此时登录失败的次数为【" + loginEventList.size() + "】不足20次,清空loginEventList,等待下一次检测");
loginEventList.clear();
} else if (mailMsg.getResult().equals("success") && loginEventList.size() >= 20) {
mailMsgAlarm = getMailMsgAlarm(loginEventList,mailMsg);
log.info("检测到登录成功的事件,此时窗口内连续登录失败的次数为【" + mailMsgAlarm.getFailTimes() + "】"); //一旦符合窗口内连续登录失败超过20次且下一次登录成功这个事件,则清空此时的loginEventList并将当前登录成功的事件进行告警推送;
loginEventList.clear();
doAlarmPush(mailMsgAlarm); collector.collect(mailMsgAlarm);//将当前登录成功的事件进行收集上报
} else {
log.info(mailMsg.getUser() + "当前已连续:【" + loginEventList.size() + "】 次登录失败");
}
}
} /**
* 2022年6月17日15:03:06
* @param eventList:当前窗口内的事件列表
* @param eventCurrent:当前登录成功的事件
* @return mailMsgAlarm:告警消息体
*/
public static MailMsgAlarm getMailMsgAlarm(List<MailMsg> eventList,MailMsg eventCurrent){ String alarmKey = eventCurrent.getUser() + "@" + eventCurrent.getClient_ip();
String loginFailStartTime = eventList.get(0).getTimestamp_datetime();
String loginSuccessTime = eventCurrent.getTimestamp_datetime();
int loginFailTimes = eventList.size(); MailMsgAlarm mailMsgAlarm = new MailMsgAlarm();
mailMsgAlarm.setMailMsg(eventCurrent);
mailMsgAlarm.setAlarmKey(alarmKey);
mailMsgAlarm.setStartTime(loginFailStartTime);
mailMsgAlarm.setEndTime(loginSuccessTime);
mailMsgAlarm.setFailTimes(loginFailTimes); return mailMsgAlarm;
} /**
* 2022年6月17日14:47:53
* @param mailMsgAlarm :当前构建的需要告警的事件
*/
public void doAlarmPush(MailMsgAlarm mailMsgAlarm){
String userKey = mailMsgAlarm.getAlarmKey();
String clientIp = mailMsgAlarm.mailMsg.getClient_ip();
boolean isWhiteListIp = IPUtils.isWhiteListIp(clientIp);
if(isWhiteListIp){//如果是白名单IP,不告警
log.info("当前登录用户【" + userKey + "】属于白名单IP");
}else {
//IP归属查询结果、企业微信推送告警
String user = HttpUtils.getUserByClientIp(clientIp);
HttpUtils.pushAlarmMsgToWechatWork(user,mailMsgAlarm.toString());
}
}
}

最后一次map算子

package com.data.dev.flink.mailTopic.OperationForLoginFailCheck;

import com.alibaba.fastjson.JSON;
import com.data.dev.common.javabean.BaseBean;
import com.data.dev.common.javabean.kafkaMailTopic.MailMsgAlarm;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.api.common.functions.MapFunction; /**
* 逻辑统计场景告警推送ES消息体
* @author wangxiaoming-ghq 2022-06-01
*/
@Slf4j
public class AlarmMsgToStringMapper extends BaseBean implements MapFunction<MailMsgAlarm, String> { @Override
public String map(MailMsgAlarm mailMsgAlarm) throws Exception {
return JSON.toJSONString(mailMsgAlarm);
}
}

ElasticSearch工具类

package com.data.dev.elasticsearch;

import com.data.dev.common.javabean.BaseBean;
import com.data.dev.key.ConfigurationKey;
import com.data.dev.key.ElasticSearchKey;
import lombok.extern.slf4j.Slf4j;
import org.apache.flink.api.common.functions.RuntimeContext;
import org.apache.flink.streaming.connectors.elasticsearch.ElasticsearchSinkFunction;
import org.apache.flink.streaming.connectors.elasticsearch.RequestIndexer;
import org.apache.flink.streaming.connectors.elasticsearch7.ElasticsearchSink;
import org.apache.flink.streaming.connectors.elasticsearch7.RestClientFactory;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.Requests; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; /**
* 2022年6月17日15:15:06
* @author wangxiaoming-ghq
* Flink流计算结果写入ES公共方法
*/
@Slf4j
public class SinkToEs extends BaseBean {
public static final long serialVersionUID = 2L;
private static final HashMap<String,String> ES_PROPS_MAP = ConfigurationKey.getApplicationProps();
private static final String HOST = ES_PROPS_MAP.get(ConfigurationKey.ES_HOST);
private static final String PASSWORD = ES_PROPS_MAP.get(ConfigurationKey.ES_PASSWORD);
private static final String USERNAME = ES_PROPS_MAP.get(ConfigurationKey.ES_USERNAME);
private static final String PORT = ES_PROPS_MAP.get(ConfigurationKey.ES_PORT); /**
* 2022年6月17日15:17:55
* 获取ES连接信息
* @return esInfoMap:ES连接信息持久化
*/
public static HashMap<String,String > getElasticSearchInfo(){
log.info("获取ES连接信息:【 " + "HOST="+HOST + "PORT="+PORT+"USERNAME="+USERNAME+"PASSWORD=********" + " 】");
HashMap<String,String> esInfoMap = new HashMap<>();
esInfoMap.put(ElasticSearchKey.HOST,HOST);
esInfoMap.put(ElasticSearchKey.PASSWORD,PASSWORD);
esInfoMap.put(ElasticSearchKey.USERNAME,USERNAME);
esInfoMap.put(ElasticSearchKey.PORT,PORT); return esInfoMap;
} /**
* @param esIndexName:写入索引名称
* @param esType:写入索引类型
* @return ElasticsearchSink.Builder<String>:构建器
*/
public static ElasticsearchSink.Builder<String> getEsSinkBuilder(String esIndexName,String esType){
HashMap<String, String> esInfoMap = getElasticSearchInfo();
List<HttpHost> httpHosts = new ArrayList<>();
httpHosts.add(new HttpHost(String.valueOf(esInfoMap.get(ElasticSearchKey.HOST)), Integer.parseInt(esInfoMap.get(ElasticSearchKey.PORT)), "http")); ElasticsearchSink.Builder<String> esSinkBuilder = new ElasticsearchSink.Builder<>(
httpHosts,
new ElasticsearchSinkFunction<String>() { public IndexRequest createIndexRequest() {
Map<String, String> json = new HashMap<>();
//log.info("写入ES的data:【"+json+"】");
IndexRequest index = Requests.indexRequest()
.index(esIndexName)
.type(esType)
.source(json);
return index;
} @Override
public void process(String element, RuntimeContext ctx, RequestIndexer indexer) {
indexer.add(createIndexRequest());
}
}
); //定义es的连接配置 带用户名密码
RestClientFactory restClientFactory = restClientBuilder -> {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(
String.valueOf(esInfoMap.get(ElasticSearchKey.USERNAME)),
String.valueOf(esInfoMap.get(ElasticSearchKey.PASSWORD))
)
);
restClientBuilder.setHttpClientConfigCallback(httpAsyncClientBuilder -> {
httpAsyncClientBuilder.disableAuthCaching();
return httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
});
}; esSinkBuilder.setRestClientFactory(restClientFactory);
return esSinkBuilder;
} }

事件实体类

package com.data.dev.common.javabean.kafkaMailTopic;

import com.data.dev.common.javabean.BaseBean;
import lombok.Data; import java.util.Objects; /**
* @author wangxiaoming-ghq 2022-05-15
* 逻辑统计场景告警事件
*/
@Data
public class MailMsgAlarm extends BaseBean { /**
* 当前登录成功的事件
*/
public MailMsg mailMsg; /**
* 当前捕获的告警主键:username@client_ip
*/
public String alarmKey; /**
* 第一次登录失败的事件时间
*/
public String startTime; /**
* 连续登录失败后下一次登录成功的事件时间
*/
public String endTime; /**
* 连续登录失败的次数
*/
public int failTimes; @Override
public String toString() {
return "{" +
" 'mailMsg_login_success':'" + mailMsg + "'" +
", 'alarmKey':'" + alarmKey + "'" +
", 'start_login_time_in3min':'" +startTime + "'" +
", 'end_login_time_in3min':'" +endTime + "'" +
", 'login_fail_times':'" +failTimes + "'" +
"}";
} public MailMsgAlarm() {
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MailMsgAlarm)) return false;
MailMsgAlarm that = (MailMsgAlarm) o;
return getFailTimes() == that.getFailTimes() && getMailMsg().equals(that.getMailMsg()) && getAlarmKey().equals(that.getAlarmKey()) && getStartTime().equals(that.getStartTime()) && getEndTime().equals(that.getEndTime());
} @Override
public int hashCode() {
return Objects.hash(getMailMsg(), getAlarmKey(), getStartTime(), getEndTime(), getFailTimes());
}
}

消息实体类

package com.data.dev.common.javabean.kafkaMailTopic;

import com.data.dev.common.javabean.BaseBean;
import lombok.Data; import java.util.Objects; /**
* {
* "user": "wangxm",
* "client_ip": "110.68.6.182",
* "source": "login",
* "loginname": "wangxm@test.com",
* "IP": "110.8.148.58",
* "timestamp": "17:58:12",
* "@timestamp": "2022-04-20T09:58:13.647Z",
* "ip": "110.7.231.25",
* "clienttype": "POP3",
* "result": "success",
* "@version": "1"
* }
*
* user登录用户
* client_ip 来源ip
* source 类型
* loginname 登录用户邮箱地址
* ip 目标前端ip
* timestamp 发送时间
* @timestamp 发送日期时间
* IP 邮件日志发送来源IP
* clienttype 客户端登录类型
* result 登录状态
*/ @Data
public class MailMsg extends BaseBean {
public String user;
public String client_ip;
public String source;
public String loginName;
public String mailSenderSourceIp;
public String timestamp_time;
public String timestamp_datetime;
public String ip;
public String clientType;
public String result;
public String version; public MailMsg() {
} public MailMsg(String user, String client_ip, String source, String loginName, String mailSenderSourceIp, String timestamp_time, String timestamp_datetime, String ip, String clientType, String result, String version) {
this.user = user;
this.client_ip = client_ip;
this.source = source;
this.loginName = loginName;
this.mailSenderSourceIp = mailSenderSourceIp;
this.timestamp_time = timestamp_time;
this.timestamp_datetime = timestamp_datetime;
this.ip = ip;
this.clientType = clientType;
this.result = result;
this.version = version;
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MailMsg)) return false;
MailMsg mailMsg = (MailMsg) o;
return getUser().equals(mailMsg.getUser()) && getClient_ip().equals(mailMsg.getClient_ip()) && getSource().equals(mailMsg.getSource()) && getLoginName().equals(mailMsg.getLoginName()) && getMailSenderSourceIp().equals(mailMsg.getMailSenderSourceIp()) && getTimestamp_time().equals(mailMsg.getTimestamp_time()) && getTimestamp_datetime().equals(mailMsg.getTimestamp_datetime()) && getIp().equals(mailMsg.getIp()) && getClientType().equals(mailMsg.getClientType()) && getResult().equals(mailMsg.getResult()) && getVersion().equals(mailMsg.getVersion());
} @Override
public int hashCode() {
return Objects.hash(getUser(), getClient_ip(), getSource(), getLoginName(), getMailSenderSourceIp(), getTimestamp_time(), getTimestamp_datetime(), getIp(), getClientType(), getResult(), getVersion());
} @Override
public String toString() {
return "{" +
" 'user':'" + user + "'" +
", 'client_ip':'" + client_ip + "'" +
", 'source':'" + source + "'" +
", 'loginName':'" + loginName + "'" +
", 'IP':'" + mailSenderSourceIp + "'" +
", 'timestamp':'" + timestamp_time + "'" +
", '@timestamp':'" + timestamp_datetime + "'" +
", 'ip':'" + "'" +
", 'clientType':'" + clientType + "'" +
", 'result':'" + result + "'" +
", 'version':'" + version + "'" +
"}";
} }

基于FLink实现的实时安全检测(一段时间内连续登录失败20次后,下一次登录成功场景)的更多相关文章

  1. 基于flink快速开发实时TopN程序

    TopN 是统计报表和大屏非常常见的功能,主要用来实时计算排行榜.流式的TopN可以使业务方在内存中按照某个统计指标(如出现次数)计算排名并快速出发出更新后的排行榜. 我们以统计词频为例展示一下如何快 ...

  2. Lyft 基于 Flink 的大规模准实时数据分析平台(附FFA大会视频)

    摘要:如何基于 Flink 搭建大规模准实时数据分析平台?在 Flink Forward Asia 2019 上,来自 Lyft 公司实时数据平台的徐赢博士和计算数据平台的高立博士分享了 Lyft 基 ...

  3. 字节跳动流式数据集成基于Flink Checkpoint两阶段提交的实践和优化

    背景 字节跳动开发套件数据集成团队(DTS ,Data Transmission Service)在字节跳动内基于 Flink 实现了流批一体的数据集成服务.其中一个典型场景是 Kafka/ByteM ...

  4. 趣头条基于 Flink 的实时平台建设实践

    本文由趣头条实时平台负责人席建刚分享趣头条实时平台的建设,整理者叶里君.文章将从平台的架构.Flink 现状,Flink 应用以及未来计划四部分分享. 一.平台架构 1.Flink 应用时间线 首先是 ...

  5. 基于Flink和规则引擎的实时风控解决方案

    案例与解决方案汇总页:阿里云实时计算产品案例&解决方案汇总 对一个互联网产品来说,典型的风控场景包括:注册风控.登陆风控.交易风控.活动风控等,而风控的最佳效果是防患于未然,所以事前事中和事后 ...

  6. DataPipeline王睿:业务异常实时自动化检测 — 基于人工智能的系统实战

    大家好,先自我介绍一下,我是王睿.之前在Facebook/Instagram担任AI技术负责人,现在DataPipeline任Head of AI,负责研发企业级业务异常检测产品,旨在帮助企业一站式解 ...

  7. 轻装上阵Flink--在IDEA上开发基于Flink的实时数据流程序

    前言 本文介绍如何在IDEA上快速开发基于Flink框架的DataStream程序.先直接上手! 环境清单 案例是在win7运行.安装VirtualBox,在VirtualBox上安装Centos操作 ...

  8. 基于flink和drools的实时日志处理

    1.背景 日志系统接入的日志种类多.格式复杂多样,主流的有以下几种日志: filebeat采集到的文本日志,格式多样 winbeat采集到的操作系统日志 设备上报到logstash的syslog日志 ...

  9. [OpenCV]基于特征匹配的实时平面目标检测算法

    一直想基于传统图像匹配方式做一个融合Demo,也算是对上个阶段学习的一个总结. 由此,便采购了一个摄像头,在此基础上做了实时检测平面目标的特征匹配算法. 代码如下: # coding: utf-8 ' ...

  10. 美团点评基于 Flink 的实时数仓建设实践

    https://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651749037&idx=1&sn=4a448647b3dae5 ...

随机推荐

  1. ORCL 时间

    一.计算时间差 两个Date类型字段:START_DATE,END_DATE,计算这两个日期的时间差(分别以天,小时,分钟,秒,毫秒): 天: ROUND(TO_NUMBER(END_DATE - S ...

  2. 安装mySql 出现 one more product requirements have not been satisified

    安装mySql 出现 one more product requirements have not been satisified 原因是缺少一些依赖环境. 在弹出的对话框中点击 否. 然后点击执行, ...

  3. 第一章:使用函数绘制matplotlib的图表组成元素

    1.绘制直线图 1 # ============================展现变量的趋势变化========================== 2 import matplotlib.pypl ...

  4. hexo-通过-metaweblog-api-同步各大博客网站

    闲聊 不多逼逼了.上干货 如何写一篇文章同步到多个博客网站 最近通过hexo 建立了博客网站,发现流量少的可怜,那把文章发到各个博客网站呢,我又懒那通过一番研究 终于搞定了通过MetaWebLog A ...

  5. 使用mysqldump备份与还原的mysql数据库

    使用mysqldump备份与还原的mysql数据库 一.mysqldump命令介绍 1.mysqldump -help 查看命令介绍: mysqldump --help 2.mysqldump登录选项 ...

  6. 记录一次缓存引起的线上BUG

    背景 有一个需求大概是这样的,为了提高推荐系统的性能,需要本来从A服务获取的帖子信息,改为从Redis里面重新读取 Redis里面没有存帖子的所有信息,只存储了推荐系统必要的字段 大概是这样的: 至于 ...

  7. 论文翻译:2022_DNS_1th:Multi-scale temporal frequency convolutional network with axial attention for speech enhancement

    论文地址:带轴向注意的多尺度时域频率卷积网络语音增强 论文代码:https://github.com/echocatzh/MTFAA-Net 引用:Zhang G, Yu L, Wang C, et ...

  8. ChatGPT杀疯了,这人工智能也太离谱了吧

    转载请注明出处️ 作者:测试蔡坨坨 原文链接:caituotuo.top/2ac8440d.html 你好,我是测试蔡坨坨. 这几天被ChatGPT刷屏,各大网站平台都能看到关于它的文章和视频,上线短 ...

  9. CH334U与FE1.1S功能对比

    CH334U与FE1.1S功能对比 介绍: CH334U是符合 USB2.0 协议规范的高性能MTT 4 端口 USB2.0  HUB 控制器芯片,高ESD特性,工业级设计,外围精简,可应用于计算机和 ...

  10. 如何用 30s 给面试官讲清楚什么是 Token

    引言 前文介绍了 Session-Cookie 的认证过程,简单回顾下基本步骤: 客户端(浏览器)向服务器发送用户名和密码 服务器验证通过后,创建 Session 对象,在 Session 中保存该用 ...