还在为开发Flink流处理应用程序时无法像开发Spring Boot程序那么优雅的分层以及装配Bean而烦恼吗?

你可能面临如下苦恼:

  1. 开发的Flink流处理应用程序,业务逻辑全部写在Flink的操作符中,代码无法服用,无法分层
  2. 要是有一天它可以像开发Spring Boot程序那样可以优雅的分层,优雅的装配Bean,不需要自己new对象好了
  3. 可以使用各种Spring生态的框架,一些琐碎的逻辑不再硬编码到代码中。

GitHub最近超火的一款开源框架,懒松鼠Flink-Boot脚手架,该脚手架简直是Spring开发工程师的福音,完美融合Spring生态体系,再也不需要手动在Java类中创建臃肿的Java对象,简直是开发大型流处理应用程序的必不可少的工具。地址:懒松鼠Flink-Boot 脚手架由《深入理解Flink核心设计与实践原理》作者开发。

接口缓存

你的现状

static Map<String,String> cache=new HashMap<String,String>();

public String findUUID(FlowData flowData) {
String value=cache.get(flowData.getSubTestItem());
if(value==null)
{
String uuid=userMapper.findUUID(flowData);
cache.put(uuid,value);
return uuid;
}
return value;
}

你想要的是这样

@Cacheable(value = "FlowData.findUUID", key = "#flowData.subTestItem")
public String findUUID(FlowData flowData) {
return userMapper.findUUID(flowData);
}

重试机制

你的现状

public void insertFlow(FlowData flowData) {
try{
userMapper.insertFlow(flowData);
}Cache(Exception e)
{
Thread.sleep(10000);
userMapper.insertFlow(flowData);
}
}

你想要的是这样

    @Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 1.5))
@Override
public void insertFlow(FlowData flowData) {
userMapper.insertFlow(flowData);
}

Bean校验

你的现状

if(flowData.getSubTestItem().length()<2&&flowData.getSubTestItem().length()>7)
{
return null;
}
if(flowData.getBillNumber()==null)
{
return null;
}

你想要的是这样

Map<String, StringBuffer> validate = ValidatorUtil.validate(flowData);
if (validate != null) {
System.out.println(validate);
return null;
} public class FlowData { private String uuid;
//声明该参数的校验规则字符串长度必须在7到20之间
@Size(min = 7, max = 20, message = "长度必须在{min}-{max}之间")
private String subTestItem;
//声明该参数的校验规则字符串不能为空
@NotBlank(message = "billNumber不能为空")
private String billNumber;
}

等等......

GitHub最近超火的一款开源框架,懒松鼠Flink-Boot脚手架,该脚手架简直是Spring开发工程师的福音,完美融合Spring生态体系,再也不需要手动在Java类中创建臃肿的Java对象,简直是开发大型流处理应用程序的必不可少的工具。懒松鼠Flink-Boot 脚手架由《深入理解Flink核心设计与实践原理》作者开发。

它为流计算开发工程师解决了

  1. 将所有对象的创建和依赖关系的维护工作都交给Spring容器的管理,降低了对象之间的耦合性,使代码变得更简洁,拒绝臃肿。
  2. 消除在工程中对单例的过多使用。
  3. 声明式事务处理,通过配置就可以完成对事物的管理,而无须手动编程。
  4. 声明式注解,可以通过注解定义方法的缓冲功能,无序手动编程。
  5. 注解式定义Bean对象的校验规则,通过注解即可完成对对象的参数校验,无序手动编程。
  6. 集成MyBatis ORM框架,注解式维护实例对象的依赖关系。
  7. 解耦Flink SQL,SQL语句剥离出JAVA文件,以简洁的模式表现在XML文件中。
  8. 封装Flink API,仅提供业务方法去编写,Spring生态融合全部搞定,无需操心。

有了它你的代码就像这样子:

/**
* github地址: https://github.com/intsmaze
* 博客地址:https://www.cnblogs.com/intsmaze/
* 出版书籍《深入理解Flink核心设计与实践原理》 随书代码
* RichFlatMapFunction为Flink框架的一个通用型操作符(算子),开发者一般在该算子的flatMap方法中编写业务逻辑
* @auther: intsmaze(刘洋)
* @date: 2020/10/15 18:33
*/
public class MybatisFlatMap extends RichFlatMapFunction<String, String> { private static Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create(); protected ApplicationContext beanFactory;
//mybatis的Service对象,操作数据库的user表
private UserService userService; @Override
public void open(Configuration parameters) {
ExecutionConfig.GlobalJobParameters globalJobParameters = getRuntimeContext()
.getExecutionConfig().getGlobalJobParameters();
beanFactory = BeanFactory.getBeanFactory((Configuration) globalJobParameters); userService = beanFactory.getBean(UserServiceImpl.class);
} @Override
public void flatMap(String value, Collector<String> out){ FlowData flowData = gson.fromJson(message, new TypeToken<FlowData>() {
}.getType());
Map<String, StringBuffer> validate = ValidatorUtil.validate(flowData);
if (validate != null) {
System.out.println(validate);
return null;
}
//数据库查询,屏蔽掉获取数据库连接,是否数据库连接,事务的声明等
String flowUUID = userService.findUUID(flowData);
if (StringUtils.isBlank(flowUUID)) {
flowUUID = UUID.randomUUID().toString();
flowData.setUuid(flowUUID);
//数据库插入,屏蔽掉获取数据库连接,是否数据库连接,事务的声明等
userService.insertFlow(flowData);
}
out.collect(gson.toJson(flowData));
}
} public interface UserService { String findUUID(FlowData flowData); void insertFlow(FlowData flowData);
} //通过注解实例化Bean对象。
@Service
//通过注解声明进行事务管理
@Transactional
//通过注解声明方法具有异常重试机制
@EnableRetry
public class UserServiceImpl implements UserService {
//通过注解进行依赖注入
@Resource
private UserMapper userMapper; @Cacheable(value = "FlowData.findUUID", key = "#flowData.subTestItem")
@Override
public String findUUID(FlowData flowData) {
return userMapper.findUUID(flowData);
} //通过注解声明该方法异常后的重试机制,无需手动编程
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 1.5))
@Override
public void insertFlow(FlowData flowData) {
userMapper.insertFlow(flowData);
}
} public interface UserMapper { String findUUID(FlowData flowData); void insertFlow(FlowData flowData);
} //注解式声明参数校验规则
public class FlowData { private String uuid;
//声明该参数的校验规则字符串长度必须在7到20之间
@Size(min = 7, max = 20, message = "长度必须在{min}-{max}之间")
private String subTestItem;
//声明该参数的校验规则字符串不能为空
@NotBlank(message = "billNumber不能为空")
private String billNumber; @NotBlank(message = "barcode不能为空")
private String barcode; private String flowName; private String flowStatus; ......
}

仓库地址:懒松鼠Flink-Boot

仓库地址:懒松鼠Flink-Boot脚手架由《深入理解Flink核心设计与实践原理》作者开发。

  1. 该脚手架屏蔽掉组装Flink API细节,让跨界变得简单,使得开发者能以传统Java WEB模式的开发方式开发出具备分布式计算能力的流处理程序。

  2. 开发者完全不需要理解分布式计算的理论知识和Flink框架的细节,便可以快速编写业务代码实现。

  3. 为了进一步提升开发者使用该脚手架开发大型项目的敏捷的程度,该脚手架工程默认集成Spring框架进行Bean管理,同时将微服务以及WEB开发领域中经常用到的框架集成进来,进一步提升开发速度。

  4. 除此之外针对目前流行的各大Java框架,该Flink脚手架工程也进行了集成,加快开发人员的编码速度,比如:

  • 集成Jbcp-template对Mysql,Oracle,SQLServer等关系型数据库的快速访问。
  • 集成Hibernate Validator框架进行参数校验。
  • 集成Spring Retry框架进行重试标志。
  • 集成Mybatis框架,提高对关系型数据库增,删,改,查的开发速度。
  • 集成Spring Cache框架,实现注解式定义方法缓存。
  • ......

1. 组织结构

Flink-Boot
├── Flink-Base -- Flink-Boot工程基础模块
├── Flink-Client -- Flink-Boot 客户端模块
├── flink-annotation -- 注解生效模块
├── flink-mybatis -- mybatis orm模块
├── flink-retry -- 注解重试机制模式
├── flink-validate -- 校验模块
├── flink-sql -- Flink SQL解耦至XML配置模块
├── flink-cache-annotation -- 接口缓冲模块
├── flink-junit -- 单元测试模块
├── flink-apollo -- 阿波罗配置客户端模块

2. 技术选项和集成情况

技术 名称 状态
Spring Framework 容器 已集成
Spring 基于XML方式配置Bean 装配Bean 已集成
Spring 基于注解方式配置Bean 装配Bean 已集成
Spring 基于注解声明方法重试机制 Retry注解 已集成
Spring 基于注解声明方法缓存 Cache注解 已集成
Hibernate Validator 校验框架 已集成
Druid 数据库连接池 已集成
MyBatis ORM框架 已集成
Kafka 消息队列 已集成
HDFS 分布式文件系统 已集成
Log4J 日志组件 已集成
Junit 单元测试 已集成
Mybatis-Plus MyBatis扩展包 进行中
PageHelper MyBatis物理分页插件 进行中
ZooKeeper 分布式协调服务 进行中
Dubbo 分布式服务框架 进行中
Redis 分布式缓存数据库 进行中
Solr & Elasticsearch 分布式全文搜索引擎 进行中
Ehcache 进程内缓存框架 进行中
sequence 分布式高效ID生产 进行中
Dubbole消费者 服务消费者 进行中
Spring eurake消费者 服务消费者 进行中
Apollo配置中心 携程阿波罗配置中心 进行中
Spring Config配置中心 Spring Cloud Config配置中心 进行中

3. 快速开始

下面是集成Spring生态的基础手册.

3.1 核心基础工程

  • flink-base :基础工程,封装了开发Flink工程的必须参数,同时集成Spring容器,为后续集成Spring各类框架提供了支撑。

    1. 可以在本地开发环境和Flink集群运行环境中随意切换。
    2. 可以在增量检查点和全量检查点之间随意切换。
    3. 内置使用HDFS作为检查点的持久存储介质。
    4. 默认使用Kafka作为数据源
    5. 内置实现了任务的暂停机制-达到任务仍在运行但不再接收Kafka数据源中的数据,代替了停止任务后再重新部署任务这一繁琐流程。
  • flink-client:业务工程,该工程依赖flink-base工程,开发任务在该工程中进行业务逻辑的开发。

3.2 Spring容器

该容器模式配置了JdbcTemplate实例,数据库连接池采用Druid,在业务方法中只需要获取容器中的JdbcTemplate实例便可以快速与关系型数据库进行交互,dataService实例封装了一些访问数据库表的方法。

topology-base.xml
<beans ......
default-lazy-init="true" default-init-method="init"> <context:property-placeholder location="classpath:config.properties"/> <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url"
value="${jdbc.url}"></property>
<property name="username" value="${jdbc.user}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="druidDataSource"></constructor-arg>
</bean> <bean id="dataService" class="com.intsmaze.flink.base.service.DataService">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean> </beans>
config.properties
jdbc.user = intsmaze
jdbc.password = intsmaze
jdbc.url = jdbc:mysql://127.0.0.1:3306/flink-boot?useUnicode=true&characterEncoding=UTF-8

3.3 启动类示例

如下是SimpleClient(com.intsmaze.flink.client.SimpleClient)类的示例代码,该类继承了BaseFlink,可以看到对应实现的方法中分别设置如下:

  • public String getTopoName():定义本作业的名称。
  • public String getConfigName():定义本作业需要读取的spring配置文件的名称
  • public String getPropertiesName():定义本作业需要读取的properties配置文件的名称。
  • public void createTopology(StreamExecutionEnvironment builder):构造本作业的拓扑结构。
/**
* github地址: https://github.com/intsmaze
* 博客地址:https://www.cnblogs.com/intsmaze/
* 出版书籍《深入理解Flink核心设计与实践原理》 随书代码
*
* @auther: intsmaze(刘洋)
* @date: 2020/10/15 18:33
*/
public class SimpleClient extends BaseFlink { public static void main(String[] args) throws Exception {
SimpleClient topo = new SimpleClient();
topo.run(ParameterTool.fromArgs(args));
} @Override
public String getTopoName() {
return "SimpleClient";
} @Override
public String getConfigName() {
return "topology-base.xml";
} @Override
public String getPropertiesName() {
return "config.properties";
} @Override
public void createTopology(StreamExecutionEnvironment builder) { DataStream<String> inputDataStrem = env.addSource(new SimpleDataSource()); DataStream<String> processDataStream = inputDataStrem.flatMap(new SimpleFunction()); processDataStream.print("输出结果");
} }

3.4 数据源

采用自定义数据源,用户需要编写自定义DataSource类,该类需要继承XXX抽象类,实现如下方法。

  • public abstract void open(StormBeanFactory beanFactory):获取本作业在Spring配置文件中配置的bean对象。
  • public abstract String sendMessage():本作业spout生成数据的方法,在该方法内编写业务逻辑产生源数据,产生的数据以String类型进行返回。
public class SimpleDataSource extends CommonDataSource {

    private static Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
...... @Override
public void open(Configuration parameters) throws Exception {
super.open(parameters);
...//构造读取各类外部系统数据的连接实例
} @Override
public String sendMess() throws InterruptedException {
Thread.sleep(1000);
......
MainData mainData = new MainData();
......//通过外部系统数据的连接实例读取外部系统数据,封装进MainData对象中,然后返回即可。
return gson.toJson(mainData);
}
}

3.5 业务逻辑实现

本作业计算的业务逻辑在Flink转换操作符中进行实现,一般来说开发者只需要实现flatMap算子即可以满足大部分算子的使用。

用户编写的自定义类需要继承com.intsmaze.flink.base.transform.CommonFunction抽象类,均需实现如下方法。

  • public abstract String execute(String message):本作业业务逻辑计算的方法,参数message为Kafka主题中读取过来的参数,默认参数为String类型,如果需要将处理的数据发送给Kakfa主题中,则要通过return将处理的数据返回即可。
public class SimpleFunction extends CommonFunction {

    private static Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();

    @Override
public String execute(String message) throws Exception {
FlowData flowData = gson.fromJson(message, new TypeToken<FlowData>() {
}.getType()); String flowUUID = dataService.findUUID(flowData);
if (StringUtils.isBlank(flowUUID)) {
flowUUID = UUID.randomUUID().toString();
flowData.setUuid(flowUUID);
dataService.insertFlow(flowData);
}
return gson.toJson(flowData);
}
}
CommonFunction

CommonFunction抽象类中默认在open方法中通过BeanFactory对象获取到了Spring容器中对于的dataService实例,对于Spring中的其他实例同理在SimpleFunction类中的open方法中获取即可。

public abstract class CommonFunction extends RichFlatMapFunction<String, String> {

    private IntCounter numLines = new IntCounter();

    protected DataService dataService;

    protected ApplicationContext beanFactory;

    @Override
public void open(Configuration parameters) {
getRuntimeContext().addAccumulator("num-FlatMap", this.numLines); ExecutionConfig.GlobalJobParameters globalJobParameters = getRuntimeContext()
.getExecutionConfig().getGlobalJobParameters();
beanFactory = BeanFactory.getBeanFactory((Configuration) globalJobParameters); dataService = beanFactory.getBean(DataService.class);
} @Override
public void flatMap(String value, Collector<String> out) throws Exception {
this.numLines.add(1);
String execute = execute(value);
if (StringUtils.isNotBlank(execute)) {
out.collect(execute);
}
} public abstract String execute(String message) throws Exception; }

可以根据情况选择重写open(Configuration parameters)方法,同时重写的open(Configuration parameters)方法的第一行要调用父类的open(Configuration parameters)方法。

public void open(Configuration parameters){
super.open(parameters);
......
//获取在Spring配置文件中配置的实例
XXX xxx=beanFactory.getBean(XXX.class);
}

3.6 集群/本地运行

在自定义的Topology类编写Main方法,创建自定义的Topology对象后,调用对象的run(...)方法。

public class SimpleClient extends BaseFlink {

/**
* 本地启动参数 -isLocal local
* 集群启动参数 -isIncremental isIncremental
*/
public static void main(String[] args) throws Exception {
SimpleClient topo = new SimpleClient();
topo.run(ParameterTool.fromArgs(args));
} .......

再也不担心写出臃肿的Flink流处理程序啦,发现一款将Flink与Spring生态完美融合的脚手架工程-懒松鼠Flink-Boot的更多相关文章

  1. 懒松鼠Flink-Boot(Flink+Spring):一款将Flink与Spring生态完美融合的脚手架工程

    目录 你可能面临如下苦恼: 接口缓存 重试机制 Bean校验 等等...... 它为流计算开发工程师解决了 有了它你的代码就像这样子: 仓库地址:懒松鼠Flink-Boot 1. 组织结构 2. 技术 ...

  2. Swing:LookAndFeel 教程第一篇——手把手教你写出自己的 LookAndFeel

    本文是 LookAndFeel 系列教程的第一篇. 是我在对 Swing 学习摸索中的一些微薄经验. 我相信,细致看全然系列之后.你就能写出自己的 LookAndFeel. 你会发现 Swing 原来 ...

  3. [置顶] 【机器学习PAI实践十一】机器学习PAI为你自动写歌词,妈妈再也不用担心我的freestyle了(提供数据、代码

    背景 最近互联网上出现一个热词就是"freestyle",源于一个比拼rap的综艺节目.在节目中需要大量考验选手的freestyle能力,freestyle指的是rapper即兴的 ...

  4. 机器学习PAI为你自动写歌词,妈妈再也不用担心我的freestyle了(提供数据、代码)

    背景 最近互联网上出现一个热词就是“freestyle”,源于一个比拼rap的综艺节目.在节目中需要大量考验选手的freestyle能力,freestyle指的是rapper即兴的根据一段主题讲一串r ...

  5. 从 demo 到生产 - 手把手写出实战需求的 Flink 广播程序

    Flink 广播变量在实时处理程序中扮演着很重要的角色,适当的使用广播变量会大大提升程序处理效率. 本文从简单的 demo 场景出发,引入生产中实际的需求并提出思路与部分示例代码,应对一般需求应该没有 ...

  6. 如何写出无法维护的代码(JAVA版)

    程序命名(针对那些不能混淆的代码) 容易输入的名字.比如:Fred,asdf 单字母的变量名.比如:a,b,c, x,y,z,或者干脆上中文比如(阿隆索肯德基) 有创意地拼写错误.比如:SetPint ...

  7. 多维度对比5款主流分布式MQ消息队列,妈妈再也不担心我的技术选型了

    1.引言 对于即时通讯网来说,所有的技术文章和资料都在围绕即时通讯这个技术方向进行整理和分享,这一次也不例外.对于即时通讯系统(包括IM.消息推送系统等)来说,MQ消息中件间是非常常见的基础软件,但市 ...

  8. 教会舍友玩 Git (再也不用担心他的学习)

    舍友长大想当程序员,我和他爷爷奶奶都可高兴了,写他最喜欢的喜之郎牌Git文章,学完以后,再也不用担心舍友的学习了(狗头)哪里不会写哪里 ~~~ 一 先来聊一聊 太多东西属于,总在用,但是一直都没整理的 ...

  9. Guava - 拯救垃圾代码,写出优雅高效,效率提升N倍

    最近在看一个同学代码的时候,发现代码中大量使用了 Google 开源的 Guava 核心库中的内容,让代码简单清晰了不少,故学习分享出 Guava 中我认为最实用的功能. Guava 项目是 Goog ...

随机推荐

  1. Python列表lists索引关于字符串小纪

    看的出'字符串列表'中的空格也是计算在内的

  2. 家庭版window10找不到文件'gpedit.msc'。请确定文件名是否正确后 ,再试一次

    今天遇到电脑找不到gpedit.msc文件,所以记录一下这个问题的解决方法 1. 首先建立一个空白文档  代码如下: @echo off pushd "%~dp0" dir /b ...

  3. Linux的内部命令和外部命令

    为了提高系统运行效率,将经常使用的轻量的命令在系统启动时一并加载这些命令到内存中供shell随时调用,这部分命令即为内部命令.反之,只有当被调用时才会被硬盘加载的这部分命令即为外部命令. 内部命令实际 ...

  4. 主题包含一张index.html

    有半年之久没有更新新作品了,但这个小小领地我并没有忘记,我会坚持下去,一直在这等你,等你的每次回眸,感恩你的每次驻足,这已经足够成为我坚守的动力和理由,尽管现在有很多不足和不尽人意,也没很多的时间管理 ...

  5. Mac环境MySql初始密码设置

    1. 首先 点击系统偏好设置 -> 点击MySQL, 在弹出的页面中,关闭服务.2. 进入终端命令输出: cd /usr/local/mysql/bin/ 命令,回车.3. 回车后,输入命令:s ...

  6. 分布式监控系统之Zabbix基础

    1.为什么要使用监控系统? 我们知道一个系统不管怎么讲它都会出故障,我们为了保证线上业务的最大化的可用性,通常我们要给关键业务做高可用:做高可用的目的是为了让故障发生时,能够有一个备用的解决方案,将故 ...

  7. uiautomatorviewer 启动报错

    我的sdk是随着AndroidStudio中下载下来的,这样做是有好处的,建议直接装个AndroidStudio这样管理sdk很方便,虽然很大,但是总比后期发现有问题好一点.最近在研究Appium要定 ...

  8. 微服务下的持续集成-Jenkins自动化部署GitHub项目

    @ 目录 一.前言 二.DevOps概念 三.为什么要做持续集成 四.常见云服务 五.手动部署Jenkins 5.1 准备工作 5.2 下载 5.3 启动 5.4 配置 5.5 Jenkins 首页 ...

  9. MySQL_where和having的区别

    1. where和having都可以使用的场景 select goods_price,goods_name from sw_goods where goods_price > 100 selec ...

  10. 面试阿里,字节跳动99%会被问到的java线程和线程池,看完这篇你就懂了!

    前言: 最近也是在后台收到很多小伙伴私信问我线程和线程池这一块的问题,说自己在面试的时候老是被问到这一块的问题,被问的很头疼.前几天看到后帮几个小伙伴解决了问题,但是问的人有点多我一个个回答也回答不过 ...