不会java但是想用flink,会python就够了 !PyFlink【一、进入pyflink的世界】

专栏目标

  1. 通过一个代码样例开始使用pyflink
  2. 通过阅读pyflink的源码,逐步了解flink的python接口实现

本文使用的flink版本和pyflink版本基于1.10.1

初识Flink

flink作为当前最流行的流批统一的数据计算处理框架,其开箱即用的部署方式(standalone)对于刚刚接触flink的人来说是非常友好和吸引人的。你可以通过地址找到你想要的版本,也可以直接下载编译好的包来进行下载,当然scala源码包也可以下载

flink的部署非常简单,如果你下载好了,你可以直接切换到解压后的目录下 并执行./bin/start-cluster.sh,默认端口为8080
浏览器打开访问一下试试,因为我这边8080被占用,flink会自动往后使用端口,所以我这边是8081
在这里插入图片描述

邂逅PyFlink

PyFlink 是什么?这个问题也许会让人感觉问题的答案太明显了,那就是 Flink + Python,也就是 Flink on Python。那么到底 Flink on Python 意味着这什么呢?那么一个非常容易想到的方面就是能够让 Python 用享受到 Flink 的所有功能。其实不仅如此,PyFlink 的存在还有另外一个非常重要的意义就是,Python on Flink,我们可以将 Python 丰富的生态计算能力运行在 Flink 框架之上,这将极大的推动 Python 生态的发展。其实,如果你再仔细深究一下,你会发现这个结合并非偶然。
在这里插入图片描述
Python 生态和大数据生态
Pythoh 生态与大数据生态有密不可分的关系,我们先看看大家都在用 Python 解决什么实际问题?通过一份用户调查我们发现,大多数 Python 用户正在解决 ”数据分析“,”机器学习“的问题,那么这些问题场景在大数据领域也有很好的解决方案。那么 Python 生态和大数据生态结合,抛开扩大大数据产品的受众用户之外,对 Python 生态一个特别重要到意义就是单机到分布式的能力增强,我想,这也是大数据时代海量数据分析对 Python 生态的强需求。

阿里技术专家金竹的案例-PyFlink实现CDN日志实时分析

需求说明

将kafka的实时数据经过group 操作之后,将聚合结果存入mysql

代码目录

相信你已经对pyflink以及该项目有了一些自己的认识了,下边直接上代码。初学一个框架的时候,首先看实现效果,这样能快速的有个认知和学习欲望。
记得首先在你的python环境上安装一下pyflink

准备工作-安装pyflink
python3 -m pip install apache-flink
准备工作-准备一个kafka环境以及zk
准备工作-准备connector的包

Flink默认是没有打包connector的,所以我们需要下载各个connector所需的jar包并放入PyFlink的lib目录。首先拿到PyFlink的lib目录的路径:

$ PYFLINK_LIB=`python -c "import pyflink;import os;print(os.path.dirname(os.path.abspath(pyflink.__file__))+'/lib')"` 

然后想需要的jar包下载到lib目录中去:

$ cd $PYFLINK_LIB 
$ curl -O https://repo1.maven.org/maven2/org/apache/flink/flink-sql-connector-kafka_2.11/1.10.0/flink-sql-connector-kafka_2.11-1.10.0.jar 
$ curl -O https://repo1.maven.org/maven2/org/apache/flink/flink-jdbc_2.11/1.10.0/flink-jdbc_2.11-1.10.0.jar 
$ curl -O https://repo1.maven.org/maven2/org/apache/flink/flink-csv/1.10.0/flink-csv-1.10.0-sql-jar.jar 
$ curl -O https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.19/mysql-connector-java-8.0.19.jar 

程序目录如下图,只要三个文件即可
在这里插入图片描述

cdn_demo.py
'''
@Date: 2020-06-07 01:00:16
@LastEditors: JingWeiZ
@LastEditTime: 2020-06-07 13:55:32
@FilePath: /flink-learning/cdn_demo.py
'''

import os

from pyflink.datastream import StreamExecutionEnvironment
from pyflink.table import StreamTableEnvironment, EnvironmentSettings
from enjoyment.cdn.cdn_udf import ip_to_province
from enjoyment.cdn.cdn_connector_ddl import kafka_source_ddl, mysql_sink_ddl

# 创建Table Environment, 并选择使用的Planner
env = StreamExecutionEnvironment.get_execution_environment()
t_env = StreamTableEnvironment.create(
    env,
    environment_settings=EnvironmentSettings.new_instance().use_blink_planner().build())

# 创建Kafka数据源表
t_env.sql_update(kafka_source_ddl)
# 创建MySql结果表
t_env.sql_update(mysql_sink_ddl)

# 注册IP转换地区名称的UDF
t_env.register_function("ip_to_province", ip_to_province)

# 核心的统计逻辑
t_env.from_path("cdn_access_log")\
    .select("uuid, "
            "client_ip as province, "  # IP 转换为地区名称
            "response_size, request_time")\
    .group_by("province")\
    .select(  # 计算访问量
    "province, count(uuid) as access_count, "
    # 计算下载总量
    "sum(response_size) as total_download,  "
    # 计算下载速度
    "sum(response_size) * 1.0 / sum(request_time) as download_speed") \
    .insert_into("cdn_access_statistic")

# 执行作业
t_env.execute("pyFlink_parse_cdn_log")

cdn_connector_ddl.py
'''
@Date: 2020-06-07 02:23:02
@LastEditors: JingWeiZ
@LastEditTime: 2020-06-07 03:07:35
@FilePath: /flink-learning/enjoyment/cdn/cdn_connector_ddl.py
'''
kafka_source_ddl = """ 
CREATE TABLE cdn_access_log ( 
 uuid VARCHAR, 
 client_ip VARCHAR, 
 request_time BIGINT, 
 response_size BIGINT, 
 uri VARCHAR 
) WITH ( 
 'connector.type' = 'kafka', 
 'connector.version' = 'universal', 
 'connector.topic' = 'access_log', 
 'connector.properties.zookeeper.connect' = '192.168.1.100:2181', 
 'connector.properties.bootstrap.servers' = '192.168.1.100:9092', 
 'format.type' = 'csv', 
 'format.ignore-parse-errors' = 'true' 
) 
"""


mysql_sink_ddl = """ 
CREATE TABLE cdn_access_statistic ( 
 province VARCHAR, 
 access_count BIGINT, 
 total_download BIGINT, 
 download_speed DOUBLE 
) WITH ( 
 'connector.type' = 'jdbc', 
 'connector.url' = 'jdbc:mysql://192.168.1.100:3306/flink', 
 'connector.table' = 'cdn_access_statistic', 
 'connector.username' = 'root', 
 'connector.password' = '123456', 
 'connector.write.flush.interval' = '1s' 
) 
"""

cdn_udf.py
'''
@Date: 2020-06-07 02:23:12
@LastEditors: JingWeiZ
@LastEditTime: 2020-06-07 02:23:29
@FilePath: /flink-learning/enjoyment/cdn/cdn_udf.py
'''
import re
import json
from pyflink.table import DataTypes
from pyflink.table.udf import udf
from urllib.parse import quote_plus
from urllib.request import urlopen


@udf(input_types=[DataTypes.STRING()], result_type=DataTypes.STRING())
def ip_to_province(ip):
    try:
        urlobj = urlopen(
            'http://whois.pconline.com.cn/ipJson.jsp?ip=%s' % quote_plus(ip))
        data = str(urlobj.read(), "gbk")
        pos = re.search("{[^{}]+\}", data).span()
        geo_data = json.loads(data[pos[0]:pos[1]])
        if geo_data['pro']:
            return geo_data['pro']
        else:
            return geo_data['err']
    except:
        return "UnKnow"

让程序跑起来

我们让代码跑在standalone的flink上边,这里我用pyflink自带的编译好的flink启动程序
本地运行作业

启动本地集群:

export PYTHONPATH=/Users/zhoujingwei/anaconda3/bin/
export PATH=$PYTHONPATH:$PATH
PYFLINK_LIB=$(python -c "import pyflink;import os;print(os.path.dirname(os.path.abspath(pyflink.__file__))+'/lib')")
$PYFLINK_LIB/../bin/start-cluster.sh

其中PYTHONPATH写上你的python执行器的具体bin目录,注意有多个python的机器上要修改为你安装了pyflink的环境
好了,flink启动之后,就要把我们的job提交到flink上

export PYTHONPATH=/Users/zhoujingwei/anaconda3/bin/
export PATH=$PYTHONPATH:$PATH
PYFLINK_LIB=$(python -c "import pyflink;import os;print(os.path.dirname(os.path.abspath(pyflink.__file__))+'/lib')")
echo $PYFLINK_LIB/../bin/
$PYFLINK_LIB/../bin/flink run -m localhost:8081 -py cdn_demo.py

然后可以看到任务已经提交到flink上了
这个时候你可以试着往kafka topic里边丢一些测试数据

cd /kafka/
./bin/kafka-console-producer.sh --broker-list 192.168.1.1:9092 --topic access_log
>abcd123,1.1.1.1,307,100000,https://www.aaa.com

然后去查看一下flink页面上是否有数据处理
在这里插入图片描述
再来看一下我们的mysql表里边有没有存进去把
在这里插入图片描述
ok,存进去了,是不是很棒棒鸡!
当然我们用的是stream的api,这个任务只要你不取消他就一直在,后边我们一起分析一下源码以及一些常用的配置。
关注我,一起学pyflink。

参考文章:

1、https://ci.apache.org/projects/flink/flink-docs-release-1.10/api/python/pyflink.datastream.html
2、https://zhuanlan.zhihu.com/p/114717285
3、https://zhuanlan.51cto.com/art/202004/614030.htm
4、https://zhuanlan.zhihu.com/p/105909554
5、https://www.alibabacloud.com/blog/the-flink-ecosystem-a-quick-start-to-pyflink_596150
6、https://flink.apache.org/downloads.html

  • 6
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
以下是使用 Python 调用 PyFlink 1.17.1 版本的 RichSinkFunction 和 SinkFunction 的示例代码: RichSinkFunction 代码示例: ```python from pyflink.common.serialization import SimpleStringEncoder from pyflink.common.typeinfo import Types from pyflink.datastream import StreamExecutionEnvironment from pyflink.datastream.connectors import StreamingFileSink from pyflink.datastream.functions import RichSinkFunction from pyflink.datastream.stream import DataStream class MySink(RichSinkFunction): def __init__(self, file_path): self.file_path = file_path def open(self, runtime_context): self.writer = open(self.file_path, 'w') def invoke(self, value, context): # 将数据写入外部存储系统 self.writer.write(str(value) + '\n') def close(self): self.writer.close() env = StreamExecutionEnvironment.get_execution_environment() env.set_parallelism(1) # 创建数据流 data_stream = env.from_collection([(1, 'hello'), (2, 'world'), (3, 'flink')], Types.TUPLE([Types.INT(), Types.STRING()])) # 定义输出路径 output_path = '/path/to/output' # 创建 StreamingFileSink sink = StreamingFileSink \ .for_row_format(output_path, SimpleStringEncoder()) \ .with_bucket_assigner(None) \ .with_bucket_check_interval(1000) \ .with_part_prefix('prefix') \ .with_part_suffix('.txt') \ .build() # 添加 sink data_stream.add_sink(MySink(output_path)).set_parallelism(1) # 执行任务 env.execute('PyFlink RichSinkFunction Example') ``` SinkFunction 代码示例: ```python from pyflink.common.serialization import SimpleStringEncoder from pyflink.common.typeinfo import Types from pyflink.datastream import StreamExecutionEnvironment from pyflink.datastream.connectors import StreamingFileSink from pyflink.datastream.functions import SinkFunction from pyflink.datastream.stream import DataStream class MySink(SinkFunction): def __init__(self, file_path): self.file_path = file_path self.writer = None def open(self, runtime_context): self.writer = open(self.file_path, 'w') def invoke(self, value, context): # 将数据写入外部存储系统 self.writer.write(str(value) + '\n') def close(self): self.writer.close() env = StreamExecutionEnvironment.get_execution_environment() env.set_parallelism(1) # 创建数据流 data_stream = env.from_collection([(1, 'hello'), (2, 'world'), (3, 'flink')], Types.TUPLE([Types.INT(), Types.STRING()])) # 定义输出路径 output_path = '/path/to/output' # 创建 StreamingFileSink sink = StreamingFileSink \ .for_row_format(output_path, SimpleStringEncoder()) \ .with_bucket_assigner(None) \ .with_bucket_check_interval(1000) \ .with_part_prefix('prefix') \ .with_part_suffix('.txt') \ .build() # 添加 sink data_stream.add_sink(MySink(output_path)).set_parallelism(1) # 执行任务 env.execute('PyFlink SinkFunction Example') ``` 在这两个示例中,我们都定义了一个自定义的 sink 类(MySink),并在其中实现了 RichSinkFunction 或 SinkFunction 接口的方法。同时,我们还使用了 StreamingFileSink 将数据写入外部存储系统。这里我们使用了 for_row_format 方法定义了输出路径、编码方式等参数,然后调用 build 方法构建 StreamingFileSink 对象,最后通过 add_sink 方法将自定义的 sink 对象添加到数据流中。最后,我们调用 execute 方法执行任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值