@@ -23,15 +23,15 @@ import akka.actor.IOManager
2323import akka .actor .Props
2424import akka .util .ByteString
2525
26- import dstream .SparkFlumeEvent
26+ import org . apache . spark . streaming . dstream .{ NetworkReceiver , SparkFlumeEvent }
2727import java .net .{InetSocketAddress , SocketException , Socket , ServerSocket }
2828import java .io .{File , BufferedWriter , OutputStreamWriter }
29- import java .util .concurrent .{TimeUnit , ArrayBlockingQueue }
29+ import java .util .concurrent .{Executors , TimeUnit , ArrayBlockingQueue }
3030import collection .mutable .{SynchronizedBuffer , ArrayBuffer }
3131import util .ManualClock
3232import org .apache .spark .storage .StorageLevel
3333import org .apache .spark .streaming .receivers .Receiver
34- import org .apache .spark .Logging
34+ import org .apache .spark .{ SparkContext , Logging }
3535import scala .util .Random
3636import org .apache .commons .io .FileUtils
3737import org .scalatest .BeforeAndAfter
@@ -44,6 +44,7 @@ import java.nio.ByteBuffer
4444import collection .JavaConversions ._
4545import java .nio .charset .Charset
4646import com .google .common .io .Files
47+ import java .util .concurrent .atomic .AtomicInteger
4748
4849class InputStreamsSuite extends TestSuiteBase with BeforeAndAfter {
4950
@@ -61,7 +62,6 @@ class InputStreamsSuite extends TestSuiteBase with BeforeAndAfter {
6162 System .clearProperty(" spark.hostPort" )
6263 }
6364
64-
6565 test(" socket input stream" ) {
6666 // Start the server
6767 val testServer = new TestServer ()
@@ -271,10 +271,49 @@ class InputStreamsSuite extends TestSuiteBase with BeforeAndAfter {
271271 val kafkaParams = Map (" zk.connect" -> " localhost:12345" ," groupid" -> " consumer-group" )
272272 val test3 = ssc.kafkaStream[String , kafka.serializer.StringDecoder ](kafkaParams, topics, StorageLevel .MEMORY_AND_DISK )
273273 }
274+
275+ test(" multi-thread receiver" ) {
276+ // set up the test receiver
277+ val numThreads = 10
278+ val numRecordsPerThread = 1000
279+ val numTotalRecords = numThreads * numRecordsPerThread
280+ val testReceiver = new MultiThreadTestReceiver (numThreads, numRecordsPerThread)
281+ MultiThreadTestReceiver .haveAllThreadsFinished = false
282+
283+ // set up the network stream using the test receiver
284+ val ssc = new StreamingContext (master, framework, batchDuration)
285+ val networkStream = ssc.networkStream[Int ](testReceiver)
286+ val countStream = networkStream.count
287+ val outputBuffer = new ArrayBuffer [Seq [Long ]] with SynchronizedBuffer [Seq [Long ]]
288+ val outputStream = new TestOutputStream (countStream, outputBuffer)
289+ def output = outputBuffer.flatMap(x => x)
290+ ssc.registerOutputStream(outputStream)
291+ ssc.start()
292+
293+ // Let the data from the receiver be received
294+ val clock = ssc.scheduler.clock.asInstanceOf [ManualClock ]
295+ val startTime = System .currentTimeMillis()
296+ while ((! MultiThreadTestReceiver .haveAllThreadsFinished || output.sum < numTotalRecords) &&
297+ System .currentTimeMillis() - startTime < 5000 ) {
298+ Thread .sleep(100 )
299+ clock.addToTime(batchDuration.milliseconds)
300+ }
301+ Thread .sleep(1000 )
302+ logInfo(" Stopping context" )
303+ ssc.stop()
304+
305+ // Verify whether data received was as expected
306+ logInfo(" --------------------------------" )
307+ logInfo(" output.size = " + outputBuffer.size)
308+ logInfo(" output" )
309+ outputBuffer.foreach(x => logInfo(" [" + x.mkString(" ," ) + " ]" ))
310+ logInfo(" --------------------------------" )
311+ assert(output.sum === numTotalRecords)
312+ }
274313}
275314
276315
277- /** This is server to test the network input stream */
316+ /** This is a server to test the network input stream */
278317class TestServer () extends Logging {
279318
280319 val queue = new ArrayBlockingQueue [String ](100 )
@@ -336,6 +375,7 @@ object TestServer {
336375 }
337376}
338377
378+ /** This is an actor for testing actor input stream */
339379class TestActor (port : Int ) extends Actor with Receiver {
340380
341381 def bytesToString (byteString : ByteString ) = byteString.utf8String
@@ -347,3 +387,36 @@ class TestActor(port: Int) extends Actor with Receiver {
347387 pushBlock(bytesToString(bytes))
348388 }
349389}
390+
391+ /** This is a receiver to test multiple threads inserting data using block generator */
392+ class MultiThreadTestReceiver (numThreads : Int , numRecordsPerThread : Int )
393+ extends NetworkReceiver [Int ] {
394+ lazy val executorPool = Executors .newFixedThreadPool(numThreads)
395+ lazy val blockGenerator = new BlockGenerator (StorageLevel .MEMORY_ONLY )
396+ lazy val finishCount = new AtomicInteger (0 )
397+
398+ protected def onStart () {
399+ blockGenerator.start()
400+ (1 to numThreads).map(threadId => {
401+ val runnable = new Runnable {
402+ def run () {
403+ (1 to numRecordsPerThread).foreach(i =>
404+ blockGenerator += (threadId * numRecordsPerThread + i) )
405+ if (finishCount.incrementAndGet == numThreads) {
406+ MultiThreadTestReceiver .haveAllThreadsFinished = true
407+ }
408+ logInfo(" Finished thread " + threadId)
409+ }
410+ }
411+ executorPool.submit(runnable)
412+ })
413+ }
414+
415+ protected def onStop () {
416+ executorPool.shutdown()
417+ }
418+ }
419+
420+ object MultiThreadTestReceiver {
421+ var haveAllThreadsFinished = false
422+ }
0 commit comments