将JavaRDD <Status>转换为JavaRDD <String>的问题

时间:2019-08-17 17:10:24

标签: java json apache-spark spark-streaming objectmapper

我正在尝试将Twitter中的推文保存到MongoDb数据库中。

我有RDD<Status>,我正在尝试通过ObjectMapper将其转换为JSON格式。但是这种转换存在一些问题(

public class Main {


    //set system credentials for access to twitter
    private static void setTwitterOAuth() {
        System.setProperty("twitter4j.oauth.consumerKey", TwitterCredentials.consumerKey);
        System.setProperty("twitter4j.oauth.consumerSecret", TwitterCredentials.consumerSecret);
        System.setProperty("twitter4j.oauth.accessToken", TwitterCredentials.accessToken);
        System.setProperty("twitter4j.oauth.accessTokenSecret", TwitterCredentials.accessTokenSecret);
    }


    public static void main(String [] args) {

        setTwitterOAuth();

        SparkConf conf = new SparkConf().setMaster("local[2]")
                                        .setAppName("SparkTwitter");
        JavaSparkContext sparkContext = new JavaSparkContext(conf);
        JavaStreamingContext jssc = new JavaStreamingContext(sparkContext, new Duration(1000));
        JavaReceiverInputDStream<Status> twitterStream = TwitterUtils.createStream(jssc);

        //Stream that contains just tweets in english
        JavaDStream<Status> enTweetsDStream=twitterStream.filter((status) -> "en".equalsIgnoreCase(status.getLang()));
        enTweetsDStream.persist(StorageLevel.MEMORY_AND_DISK());


        enTweetsDStream.print();
        jssc.start();
        jssc.awaitTermination();
    }

    static void saveRawTweetsToMondoDb(JavaRDD<Status> rdd,JavaSparkContext sparkContext) {
     try {
            ObjectMapper objectMapper = new ObjectMapper();
            SQLContext sqlContext = new SQLContext(sparkContext);
            JavaRDD<String> tweet =  rdd.map(status -> objectMapper.writeValueAsString(status));

            DataFrame dataFrame = sqlContext.read().json(tweet);

            Map<String, String> writeOverrides = new HashMap<>();
            writeOverrides.put("uri", "mongodb://127.0.0.1/forensicdb.LiveRawTweets");
            WriteConfig writeConfig = WriteConfig.create(sparkContext).withJavaOptions(writeOverrides);
            MongoSpark.write(dataFrame).option("collection", "LiveRawTweets").mode("append").save();

        } catch (Exception e) {
            System.out.println("Error saving to database");
        }
    }

JavaRDD<String> tweet =  rdd.map(status -> objectMapper.writeValueAsString(status));

这是个问题。JavaRDD<String>需要不兼​​容的类型,但映射被推断为javaRDD<R>

1 个答案:

答案 0 :(得分:1)

不幸的是,

Java类型推断并不总是很聪明,因此在这些情况下,我要做的是将lambda的所有位提取为变量,直到找到Java无法为其提供准确类型的变量为止。然后,我给该表达式指定我认为应该具有的类型,并查看Java为什么抱怨它。有时,这只是编译器中的一个限制,您必须将表达式显式“转换”为所需的类型,其他时候,您会发现代码存在问题。就您而言,代码对我来说很好,所以还必须有其他内容。

但是,我有一条评论:在这里,您只需支付一次JSON序列化的费用(从Status到JSON字符串),然后反序列化(从JSON字符串到Row)。另外,您没有为Dataset提供任何架构,因此必须进行两次数据传递(或取决于配置的数据样本)才能推断出架构。如果数据很大,所有这些都可能会非常昂贵。如果性能令人担忧并且如果Status比较简单,我建议您直接从Row转换为Status

另一个“顺便说一句”:您正在隐式序列化ObjectMapper,很可能您不想这样做。看来该类确实支持Java序列化,但是支持special logic。由于Spark的默认配置是使用Kryo(其性能比Java序列化要好得多),因此我怀疑在使用默认的FieldSerializer时它会做正确的事情。您有三种选择:

  • 将对象映射器设为静态以避免序列化
  • 配置Kryo注册器以使用Java序列化对ObjectMapper类型的对象进行序列化/反序列化。那会起作用,但不值得付出努力。
  • 在任何地方都使用Java序列化而不是Kryo。馊主意!它很慢并且会占用大量空间(内存和磁盘取决于要在其中写入序列化对象的位置)。