flink多线程程序设计 | 青训营笔记

193 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第4天。

我们确定了目标为开发一个基于流式计算框架、遵循robots协议的分布式爬虫,并能够对爬取到的数据进行解析并存储。本文章将就flink程序设计进行讲解。

下图为一个flink流式计算框架的执行流程。

从图中可以看到,一个flink计算过程需要稳定的数据源,转换操作以及输出。经过多方学习,在本项目中,我们设置:

  • 数据源:redis数据库
  • 转换操作:爬虫系统
  • 输出:redis数据库

基于此,系统的执行流程为:

到这里,系统的关键问题在于:

  • 如何从redis中读取数据
  • 如何创建新进程
  • 如何将数据写入redis中

接下来我们将结合代码分别介绍这三个问题的实现。

  • 如何从redis中读取数据
this.jedis = new Jedis("127.0.0.1", 6379); // 连接redis
while(isRunning){
    String url = null;
    while (url ==null){
        String randomDomain = jedis.srandmember(SpiderConstants.SPIDER_WEBSITE_DOMAINS_KEY);    // jd.com
        String key = randomDomain + SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX;                // jd.com.higher
        url = jedis.lpop(key); // 从高优先级队列中取出一个url
        if(url == null) {   // 如果为null,则从低优先级中获取
            key = randomDomain + SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX;    // jd.com.lower
            url = jedis.lpop(key);
        }
        SpiderUtil.sleep(5000);  // 等待一段时间,防止被反爬
    }

    sourceContext.collect(url);
  • 如何创建新线程
public class SpiderFlatMapFunction implements FlatMapFunction<String, UrlList> {
    private ISpider iSpider;
    public SpiderFlatMapFunction(ISpider iSpider){
        this.iSpider = iSpider;
    }
    @Override
    public void flatMap(String s, Collector<UrlList> collector) throws Exception {
        UrlList urlList = iSpider.startSingle(s); // 启动爬虫系统,返回获得的新url
        collector.collect(urlList); // 将获得的url存入数据库
    }
}
  • 如何将数据写入redis中
// 根据优先级的高低将数据分别存入
public void invoke(UrlList value, Context context) {
    List<String> high = value.getHighList();
    List<String> low = value.getLowList();
    if(!high.isEmpty()){
        for(String url:high){
            String domain = SpiderUtil.getTopDomain(url);   // 获取url对应的顶级域名,如jd.com
            String key = domain + SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX;            // 拼接url队列的key,如jd.com.higher
            jedis.lpush(key, url);
        }

    }
    if(!low.isEmpty()){
        for(String url:low){
            String domain = SpiderUtil.getTopDomain(url);   // 获取url对应的顶级域名,如jd.com
            String key = domain + SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX;            // 拼接url队列的key,如jd.com.higher
            jedis.lpush(key, url);
        }
    }
}

大功告成!接下来看看flink主函数是怎么设置的

public class FlinkSpider {
    public static void main(String[] args) throws Exception {
        ISpider iSpider = ISpider.getInstance();

        // 创建流式执行环境
        StreamExecutionEnvironment env =
                StreamExecutionEnvironment.getExecutionEnvironment();
        env.setParallelism(4);

        // 添加数据源
        DataStreamSource<String> stream = env.addSource(new MyRedisSource());

        // 处理数据 
        SingleOutputStreamOperator<UrlList> urlListSingleOutputStreamOperator = stream.flatMap(new SpiderFlatMapFunction(iSpider));

        urlListSingleOutputStreamOperator.addSink(new MyRedisSinkFunction());
        env.execute();
    }
}

ps. 主函数运行时务必保持redis-server在后台运行。


参考 www.runoob.com/redis/redis…