টাস্ক সিরিয়ালীকরণযোগ্য নয়: কেবলমাত্র ক্লাসে অবজেক্ট নয়, ক্লোজারের বাইরে ফাংশন কল করার সময় java.io.NotSerializableEception


224

বন্ধের বাইরে ফাংশনটি কল করার সময় অদ্ভুত আচরণ করা:

  • ফাংশন যখন কোনও বস্তুতে থাকে তখন সমস্ত কিছু কাজ করে
  • ফাংশন যখন ক্লাসে থাকে তখন পান:

কাজটি সিরিয়ালযোগ্য নয়: java.io.NotSerializableException: পরীক্ষা করা

সমস্যাটি হ'ল আমার ক্লাসে আমার কোড দরকার এবং কোনও অবজেক্ট নয়। কোন ধারণা কেন এই ঘটছে? একটি স্কালা অবজেক্ট সিরিয়ালাইজড (ডিফল্ট?)?

এটি একটি কার্যকরী কোড উদাহরণ:

object working extends App {
    val list = List(1,2,3)

    val rddList = Spark.ctx.parallelize(list)
    //calling function outside closure 
    val after = rddList.map(someFunc(_))

    def someFunc(a:Int)  = a+1

    after.collect().map(println(_))
}

এটি অ-কর্মহীন উদাহরণ:

object NOTworking extends App {
  new testing().doIT
}

//adding extends Serializable wont help
class testing {  
  val list = List(1,2,3)  
  val rddList = Spark.ctx.parallelize(list)

  def doIT =  {
    //again calling the fucntion someFunc 
    val after = rddList.map(someFunc(_))
    //this will crash (spark lazy)
    after.collect().map(println(_))
  }

  def someFunc(a:Int) = a+1
}

স্পার্ক.ক্টেক্স কী? পদ্ধতি
সিটিএক্স এএএএফটি-র

উত্তর:


334

আরডিডিগুলি সিরিয়ালাইজযোগ্য ইন্টারফেস প্রসারিত করে , তাই এটি আপনার কাজটি ব্যর্থ হওয়ার কারণ নয়। এখন এর অর্থ এই নয় যে আপনি RDDস্পার্কের সাথে একটি সিরিয়ালায়ন করতে এবং এড়াতে পারেনNotSerializableException

স্পার্ক একটি বিতরণকারী কম্পিউটিং ইঞ্জিন এবং এর প্রধান বিমূর্ততা একটি লবণাক্ত বিতরণ ডেটাসেট ( আরডিডি ), যা বিতরণকৃত সংগ্রহ হিসাবে দেখা যায়। মূলত, আরডিডি উপাদানগুলি ক্লাস্টারের নোড জুড়ে বিভক্ত হয়, তবে স্পার্ক এটিকে ব্যবহারকারী থেকে দূরে রাখে, ব্যবহারকারীকে আরডিডি (সংগ্রহ) এর সাথে ইন্টারঅ্যাক্ট করতে দেয় যেন এটি স্থানীয় কোনও one

না অনেক বিস্তারিত মধ্যে পেতে, কিন্তু যখন আপনি একটি RDD (চালু বিভিন্ন রূপান্তরের চালানো map, flatMap, filterএবং অন্যদের), আপনার রূপান্তর কোড (অবসান) হল:

  1. ড্রাইভার নোডে সিরিয়ালযুক্ত,
  2. ক্লাস্টারে উপযুক্ত নোডে প্রেরণ করা,
  3. deserialized,
  4. এবং অবশেষে নোডগুলিতে কার্যকর করা হয়েছে

আপনি অবশ্যই এটি স্থানীয়ভাবে চালাতে পারেন (যেমন আপনার উদাহরণ হিসাবে) তবে সেই সমস্ত পর্যায়গুলি (নেটওয়ার্কের মাধ্যমে শিপিং বাদে) এখনও ঘটে। [এটি আপনাকে প্রোডাক্টে মোতায়েনের আগে কোনও বাগগুলি ধরতে দেয়]

আপনার দ্বিতীয় ক্ষেত্রে যা ঘটে তা হ'ল আপনি testingমানচিত্রের অভ্যন্তর থেকে শ্রেণিতে সংজ্ঞায়িত একটি পদ্ধতি কল করছেন । স্পার্ক দেখতে পায় এবং যেহেতু পদ্ধতিগুলি নিজেরাই সিরিয়ালাইজ করা যায় না, স্পার্ক পুরো ক্লাসটিকে সিরিয়ালাইজ করার চেষ্টা করে testing, যাতে অন্য জেভিএম-তে কার্যকর হওয়ার পরে কোডটি এখনও কাজ করতে পারে work আপনার দুটি সম্ভাবনা রয়েছে:

হয় আপনি ক্লাস টেস্টিং সিরিয়ালযোগ্য করে তোলেন, তাই পুরো ক্লাসটি স্পার্কের মাধ্যমে সিরিয়ালাইজ করা যায়:

import org.apache.spark.{SparkContext,SparkConf}

object Spark {
  val ctx = new SparkContext(new SparkConf().setAppName("test").setMaster("local[*]"))
}

object NOTworking extends App {
  new Test().doIT
}

class Test extends java.io.Serializable {
  val rddList = Spark.ctx.parallelize(List(1,2,3))

  def doIT() =  {
    val after = rddList.map(someFunc)
    after.collect().foreach(println)
  }

  def someFunc(a: Int) = a + 1
}

বা আপনি someFuncকোনও পদ্ধতির পরিবর্তে ফাংশনটি তৈরি করেন (ফাংশনগুলি স্কালায় বস্তুগুলি হয়), যাতে স্পার্ক এটিকে সিরিয়ালায়িত করতে সক্ষম হবে:

import org.apache.spark.{SparkContext,SparkConf}

object Spark {
  val ctx = new SparkContext(new SparkConf().setAppName("test").setMaster("local[*]"))
}

object NOTworking extends App {
  new Test().doIT
}

class Test {
  val rddList = Spark.ctx.parallelize(List(1,2,3))

  def doIT() =  {
    val after = rddList.map(someFunc)
    after.collect().foreach(println)
  }

  val someFunc = (a: Int) => a + 1
}

একই রকম, তবে শ্রেণিকরণ সিরিয়ালাইজেশনের ক্ষেত্রে একই সমস্যাটি আপনার আগ্রহী হতে পারে না এবং আপনি এটি স্পার্ক সামিট ২০১৩ উপস্থাপনায় পড়তে পারেন ।

পার্শ্ব নোট হিসাবে, আপনি আবার লিখতে rddList.map(someFunc(_))পারেন rddList.map(someFunc), সেগুলি হুবহু এক। সাধারণত, দ্বিতীয়টি পছন্দ করা হয় কারণ এটি কম ভার্বোস এবং পরিষ্কার পড়ার মতো।

সম্পাদনা করুন (2015-03-15): Spark-5307 চালু SerializationDebugger এবং স্পার্ক 1.3.0 এটি ব্যবহার করতে প্রথম সংস্করণ। এটি একটি নোট-সিরিজযোগ্য এক্সসেপশনে সিরিয়ালাইজেশন পাথ যুক্ত করে । যখন একটি নোট-সিরিজাইজএক্সেপশন মুখোমুখি হয়, তখন ডিবাগার বস্তুটির গ্রাফ ঘুরে দেখেন যে সিরিয়ালাইজ করা যায় না এমন বস্তুর দিকে পাথ খুঁজে পেতে এবং ব্যবহারকারীকে অবজেক্টটি সন্ধান করতে সহায়তা করার জন্য তথ্য তৈরি করে।

ওপি-র ক্ষেত্রে, এটি স্টাডাউটে মুদ্রিত হয়:

Serialization stack:
    - object not serializable (class: testing, value: testing@2dfe2f00)
    - field (class: testing$$anonfun$1, name: $outer, type: class testing)
    - object (class testing$$anonfun$1, <function1>)

1
হুম, আপনি যা ব্যাখ্যা করেছেন তা অবশ্যই বোধগম্য হয় এবং ব্যাখ্যা করে কেন পুরো ক্লাসটি সিরিয়ালযুক্ত হয় (এমন কিছু যা আমি পুরোপুরি বুঝতে পারি নি)। তবুও আমি এখনও ধরে রাখব যে আরডিডি সিরিয়ালযোগ্য নয় (ভাল তারা সিরিয়ালাইজেবল প্রসারিত করে, তবে এর অর্থ এই নয় যে তারা নটসিরাইজিবলম্বনের কারণ না ঘটায়, চেষ্টা করুন)। এই কারণেই যদি আপনি তাদের ক্লাসের বাইরে রাখেন তবে এটি ত্রুটিটি ঠিক করে। আমি যা বলতে চাইছি তার সম্পর্কে আরও সুনির্দিষ্ট হওয়ার জন্য আমি আমার উত্তরটি সম্পাদনা করতে যাচ্ছি - অর্থাত্ তারা ব্যতিক্রম ঘটায়, না যে তারা ইন্টারফেসটি প্রসারিত করে।
সামিটবেস্ট

35
আপনার যদি শ্রেণীর উপর নিয়ন্ত্রণ না থাকে তবে আপনার সিরিয়ালাইজযোগ্য হতে হবে ... আপনি যদি স্কাল ব্যবহার করছেন তবে এটি কেবল সিরিয়ালাইজেবলের সাথে ইনস্ট্যান্ট করতে পারবেন:val test = new Test with Serializable
মার্ক এস

4
"rddList.map (someFunc (_)) to rddList.map (someFunc), তারা হুবহু একই" না তারা হুবহু এক নয়, এবং প্রকৃতপক্ষে পরবর্তীকালে ব্যবহারের ফলে সিরিয়ালাইজেশন ব্যতিক্রম হতে পারে পূর্বের লোকেরা তা না করত।
সামথিবেষ্ট

1
@ সাম্তিবেস্ট আপনি কি ব্যাখ্যা করতে পারবেন দয়া করে কেন মানচিত্র (কিছুFunc (_)) সিরিয়াল ব্যতিক্রম ঘটায় না যেখানে মানচিত্র (সামু ফাঙ্ক) থাকবে?
অ্যালন

31

মূল কোডটি কেন কাজ করে না এবং সমস্যাটি সমাধানের দুটি উপায় ব্যাখ্যা করার ক্ষেত্রে গ্রেগার উত্তর দুর্দান্ত। তবে এই দ্রবণটি খুব নমনীয় নয়; আপনার ক্লোজারে কোনও নন- Serializableক্লাসে কোনও মেথড কল অন্তর্ভুক্ত রয়েছে সেই ক্ষেত্রে বিবেচনা করুন যা আপনার কোনও নিয়ন্ত্রণ নেই। আপনি Serializableএই ক্লাসে ট্যাগ যুক্ত করতে পারবেন না বা পদ্ধতিটিকে কোনও কার্যে রূপান্তর করতে অন্তর্নিহিত বাস্তবায়নটি পরিবর্তন করতে পারবেন না।

নীলেশ এটির জন্য দুর্দান্ত কাজটি উপস্থাপন করে তবে সমাধানটি আরও সংক্ষিপ্ত এবং সাধারণ উভয়ই করা যেতে পারে:

def genMapper[A, B](f: A => B): A => B = {
  val locker = com.twitter.chill.MeatLocker(f)
  x => locker.get.apply(x)
}

এই ফাংশন-সিরিয়ালাইজারটি তখন ক্লোজার এবং পদ্ধতি কলগুলি স্বয়ংক্রিয়ভাবে মোড়ানোর জন্য ব্যবহার করা যেতে পারে:

rdd map genMapper(someFunc)

এই কৌশলটিতে অ্যাক্সেস করার জন্য অতিরিক্ত শার্ক নির্ভরতা প্রয়োজন না হওয়ার সুবিধাও রয়েছে KryoSerializationWrapper, যেহেতু টুইটারের চিলটি ইতিমধ্যে মূল স্পার্ক দ্বারা টানানো হয়েছে


হাই, আমি অবাক হয়েছি আমি যদি আপনার কোড ব্যবহার করি তবে আমার কি কিছু রেজিস্ট্রেশন করা দরকার? আমি চেষ্টা করেছিলাম এবং ক্রিয়ো থেকে অক্ষম শ্রেণীর ব্যতিক্রম পাই। THX
G_cy

25

সমস্যাটি সম্পূর্ণরূপে ব্যাখ্যা করার জন্য সম্পূর্ণ আলোচনা, যা এই সিরিয়ালাইজেশন সমস্যাগুলি এড়াতে দুর্দান্ত দৃষ্টান্ত পরিবর্তন করার প্রস্তাব দেয়: https://github.com/samthebest/dump/blob/master/sams-scala-tutorial/serialization-exception-and-mmory- leaks-no-ws.md

শীর্ষে ভোট দেওয়া উত্তরটি মূলত একটি সম্পূর্ণ ভাষার বৈশিষ্ট্য ছুঁড়ে ফেলার পরামর্শ দিচ্ছে - এটি এখন আর পদ্ধতিগুলি ব্যবহার করে এবং কেবল ফাংশন ব্যবহার করে না। প্রকৃতপক্ষে ক্লাসে ক্রিয়ামূলক প্রোগ্রামিং পদ্ধতিগুলি এড়ানো উচিত, তবে তাদের ফাংশনগুলিতে রূপান্তর করা এখানে নকশার সমস্যাটি সমাধান করছে না (উপরের লিঙ্কটি দেখুন)।

এই নির্দিষ্ট পরিস্থিতিতে দ্রুত সমাধান হিসাবে আপনি কেবল @transientএটোটেশনটি ব্যবহার করে এটি আপত্তিজনক মানটিকে সিরিয়াল করার চেষ্টা করবেন না তা বলতে পারেন (এখানে, Spark.ctxএকটি কাস্টম শ্রেণি যা স্পার্কের নাম অপের নাম অনুসারে নয়):

@transient
val rddList = Spark.ctx.parallelize(list)

আপনি কোডটি পুনর্গঠন করতে পারেন যাতে rddList অন্য কোথাও বাস করে, তবে এটিও বাজে।

ভবিষ্যত সম্ভবত স্পোরস হয়

ভবিষ্যতে স্কালায় "স্পোরস" নামে পরিচিত এই জিনিসগুলি অন্তর্ভুক্ত থাকবে যা আমাদের বন্ধ করার ফলে সঠিকভাবে কী করবে এবং কী করবে না তা জরিমানা দানা নিয়ন্ত্রণের অনুমতি দেয়। তদতিরিক্ত, এটি দুর্ঘটনাক্রমে অ-সিরিয়ালাইজযোগ্য প্রকারগুলিতে (বা কোনও অযাচিত মান) এখনই সংকলনের ত্রুটিগুলিতে পরিণত হওয়া উচিত যা ভয়াবহ রানটাইম ব্যতিক্রম / মেমরি ফাঁস।

http://docs.scala-lang.org/sips/pending/spores.html

ক্রিয়ো সিরিয়ালাইজেশন সম্পর্কে একটি টিপ

কাইরো ব্যবহার করার সময়, এটি তৈরি করুন যাতে নিবন্ধকরণ প্রয়োজনীয় হয়, এর অর্থ মেমরি ফাঁসের পরিবর্তে আপনি ত্রুটি পেয়েছেন:

"অবশেষে, আমি জানি যে ক্রিওর সাথে ক্রিও.সেটগ্রিজিটিওশনাল (সত্য) রয়েছে তবে এটি কীভাবে ব্যবহার করতে হবে তা নির্ধারণ করার জন্য আমার খুব কষ্ট হচ্ছে this ক্লাস। "

ক্রিয়োর সাথে ক্লাস নিবন্ধনের কৌশল

অবশ্যই এটি আপনাকে টাইপ-লেভেল নিয়ন্ত্রণ দেয় মান-স্তর নিয়ন্ত্রণ নয়।

... আরও আইডিয়া আসতে হবে।


9

আমি একটি ভিন্ন পদ্ধতির ব্যবহার করে এই সমস্যাটি সমাধান করেছি। বন্ধ করার মধ্য দিয়ে যাওয়ার আগে আপনাকে কেবল অবজেক্টগুলিকে সিরিয়ালাইজ করা এবং পরে ডি-সিরিয়ালাইজ করা দরকার। এই ক্লাসটি ক্রিয়োকে পর্দার আড়ালে ব্যবহার করে কারণ আপনার ক্লাসগুলি সিরিয়ালযোগ্যযোগ্য না হওয়া সত্ত্বেও এই পদ্ধতির কাজ করে। আপনার যা দরকার তা হল কিছু তরকারি। ;)

আমি এটি কীভাবে করেছি তার উদাহরণ এখানে:

def genMapper(kryoWrapper: KryoSerializationWrapper[(Foo => Bar)])
               (foo: Foo) : Bar = {
    kryoWrapper.value.apply(foo)
}
val mapper = genMapper(KryoSerializationWrapper(new Blah(abc))) _
rdd.flatMap(mapper).collectAsMap()

object Blah(abc: ABC) extends (Foo => Bar) {
    def apply(foo: Foo) : Bar = { //This is the real function }
}

ক্লাস, সহকর্মী অবজেক্ট, নেস্টেড ক্লাস, একাধিক তৃতীয় পক্ষের লিবার্সের উল্লেখ হিসাবে ব্লাহাকে যতটা জটিল আপনি নির্দ্বিধায় বোধ করবেন না।

ক্রিয়ো সিরিয়ালাইজেশন র্যাপার উল্লেখ করে: https://github.com/amplab/shark/blob/master/src/main/scala/shark/execution/serialization/ KryoSerializationWrapper.scala


এটি কি প্রকৃতপক্ষে উদাহরণটিকে সিরিয়ালাইজ করে দেয় বা একটি স্থির উদাহরণ তৈরি করে এবং একটি রেফারেন্স সিরিয়াল করে দেয় (আমার উত্তর দেখুন)।
সামতিবেস্ট

2
@ সাম্তেবেস্ট আপনি কি আরও বিস্তারিত বলতে পারবেন? আপনি যদি অনুসন্ধান করেন KryoSerializationWrapperতবে স্পার্ককে ভাবছে যে এটি সত্যই java.io.Serializable- এটি সহজভাবে ক্রিয়োকে ব্যবহার করে অভ্যন্তরীণভাবে সিরিয়ালাইজ করে - দ্রুত, সহজ। এবং আমি মনে করি না এটি কোনও স্থির উদাহরণের সাথে কাজ করে - যখন মান.অ্যাপ্লাই () বলা হয় তখন এটি মানটিকে ডি-সিরিয়ালাইজ করে।
নিলেশ

8

আমি অনুরূপ সমস্যার মুখোমুখি হয়েছিলাম এবং গ্রেগার উত্তর থেকে আমি যা বুঝতে পারি তা হ'ল

object NOTworking extends App {
 new testing().doIT
}
//adding extends Serializable wont help
class testing {

val list = List(1,2,3)

val rddList = Spark.ctx.parallelize(list)

def doIT =  {
  //again calling the fucntion someFunc 
  val after = rddList.map(someFunc(_))
  //this will crash (spark lazy)
  after.collect().map(println(_))
}

def someFunc(a:Int) = a+1

}

আপনার ডোইত পদ্ধতি ধারাবাহিকভাবে করার চেষ্টা করছে someFunc (_) পদ্ধতি, কিন্তু পদ্ধতি serializable নয়, এটা ধারাবাহিকভাবে বর্গ করার চেষ্টা করে পরীক্ষামূলক যা আবার serializable নয়।

সুতরাং আপনার কোডটি কাজ করুন, আপনার ডওটি পদ্ধতির অভ্যন্তরে কিছুটা নির্দিষ্ট করা উচিত । উদাহরণ স্বরূপ:

def doIT =  {
 def someFunc(a:Int) = a+1
  //function definition
 }
 val after = rddList.map(someFunc(_))
 after.collect().map(println(_))
}

এবং যদি এখানে ছবিতে একাধিক ফাংশন আসছে, তবে সেই সমস্ত ফাংশনগুলি পিতামাতার প্রসঙ্গে পাওয়া উচিত।


7

আমি সম্পূর্ণরূপে নিশ্চিত নই যে এটি স্কালার ক্ষেত্রে প্রযোজ্য তবে জাভাতে আমি NotSerializableExceptionআমার কোডটি রিফ্যাক্ট করে সমাধান করেছি যাতে বন্ধটি কোনও অ-সিরিয়ালযোগ্য finalক্ষেত্রটি অ্যাক্সেস না করে ।


আমি জাভাতেও একই সমস্যার মুখোমুখি হয়েছি, আমি আরডিডি ফোরচ পদ্ধতিতে জাভা আইও প্যাকেজ থেকে ফাইল রাইটার ক্লাসটি ব্যবহার করার চেষ্টা করছি। আপনি কি দয়া করে আমাকে জানান যে আমরা কীভাবে এটি সমাধান করতে পারি।
শঙ্কর

1
ওয়েল @Shankar, যদি FileWriterএকটি হয় finalবাইরের ক্লাসের ক্ষেত্র, আপনি এটি করতে পারবেন না। তবে FileWriterএটি দুটি Stringবা একটি বা একটি থেকে তৈরি করা যেতে পারে । সুতরাং বাইরের শ্রেণি থেকে ফাইলনামের উপর ভিত্তি করে স্থানীয় তৈরি করতে আপনার কোডটি রিফ্যাক্টর করুন । FileSerializableFileWriter
ট্রেবার অভদ্র 16

0

এফওয়াইআই স্পার্ক ২.৪-এ আপনি সম্ভবত এই সমস্যাটির মুখোমুখি হবেন। ক্রিও সিরিয়ালাইজেশন আরও ভাল হয়েছে তবে অনেক ক্ষেত্রে আপনি spark.kryo.unsafe = সত্য বা নিষ্পাপ ক্রাইও সিরিয়াল ব্যবহার করতে পারবেন না।

দ্রুত সমাধানের জন্য আপনার স্পার্ক কনফিগারেশনে নিম্নলিখিতটি পরিবর্তন করে দেখুন

spark.kryo.unsafe="false"

অথবা

spark.serializer="org.apache.spark.serializer.JavaSerializer"

আমি যে আমি সম্মুখীন কাস্টম RDD রূপান্তরের সংশোধন করতে বা ব্যক্তিগতভাবে স্পষ্ট সম্প্রচারের ভেরিয়েবল ব্যবহার করে এবং নতুন Inbuilt টুইটার-শীতলতা API ব্যবহার, তাদের কাছ থেকে রূপান্তর দ্বারা লিখতে rdd.map(row =>করার rdd.mapPartitions(partition => {ফাংশন।

উদাহরণ

পুরানো (দুর্দান্ত নয়)

val sampleMap = Map("index1" -> 1234, "index2" -> 2345)
val outputRDD = rdd.map(row => {
    val value = sampleMap.get(row._1)
    value
})

বিকল্প (আরও ভাল) উপায়

import com.twitter.chill.MeatLocker
val sampleMap = Map("index1" -> 1234, "index2" -> 2345)
val brdSerSampleMap = spark.sparkContext.broadcast(MeatLocker(sampleMap))

rdd.mapPartitions(partition => {
    val deSerSampleMap = brdSerSampleMap.value.get
    partition.map(row => {
        val value = sampleMap.get(row._1)
        value
    }).toIterator
})

এই নতুন উপায়ে কেবলমাত্র পার্টিশন প্রতি একবার সম্প্রচার পরিবর্তনকে কল করবে যা আরও ভাল। আপনি ক্লাস নিবন্ধন না করলে আপনাকে এখনও জাভা সিরিয়ালাইজেশন ব্যবহার করতে হবে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.