Cảm ơn bạn đã đặt câu hỏi nổi bật này ra khỏi đó. Vì một lý do nào đó, khi nhắc đến Spark, mọi người đều bị cuốn vào phân tích đến nỗi họ quên mất các phương pháp kỹ thuật phần mềm tuyệt vời đã xuất hiện trong 15 năm qua. Đây là lý do tại sao chúng ta nên thảo luận về thử nghiệm và tích hợp liên tục (trong số những thứ khác như DevOps) trong khóa học của mình.
Sơ lược về thuật ngữ
A bài kiểm tra đơn vị thực sự có nghĩa là bạn có toàn quyền kiểm soát mọi thành phần trong bài kiểm tra. Không thể có tương tác với cơ sở dữ liệu, cuộc gọi REST, hệ thống tệp hoặc thậm chí là đồng hồ hệ thống; mọi thứ phải được "nhân đôi" (ví dụ: chế nhạo, khai thác, v.v.) như Gerard Mezaros đưa nó vào Mẫu thử nghiệm xUnit . Tôi biết điều này có vẻ giống như ngữ nghĩa, nhưng nó thực sự quan trọng. Không hiểu đây là một trong những lý do chính khiến bạn thấy các lỗi kiểm tra không liên tục trong tích hợp liên tục.
Chúng tôi vẫn có thể kiểm tra đơn vị
Vì vậy, với sự hiểu biết này, kiểm tra đơn vị một RDD
là không thể. Tuy nhiên, vẫn có một nơi để kiểm thử đơn vị khi phát triển phân tích.
Hãy xem xét một hoạt động đơn giản:
rdd.map(foo).map(bar)
Đây foo
và bar
là các chức năng đơn giản. Chúng có thể được kiểm tra đơn vị theo cách thông thường, và chúng phải được với nhiều trường hợp góc nhất bạn có thể tập hợp. Rốt cuộc, tại sao họ lại quan tâm đến việc họ lấy đầu vào từ đâu cho dù đó là vật cố định thử nghiệm hay vật phẩm RDD
?
Đừng quên Spark Shell
Đây không phải là thử nghiệm cho mỗi người , nhưng trong những giai đoạn đầu này, bạn cũng nên thử nghiệm trong Spark shell để tìm ra các chuyển đổi của bạn và đặc biệt là hậu quả của cách tiếp cận của bạn. Ví dụ, bạn có thể kiểm tra kế hoạch truy vấn vật lý và logic, phân vùng chiến lược và bảo quản, và tình trạng của dữ liệu của bạn với nhiều chức năng khác nhau như toDebugString
, explain
, glom
, show
, printSchema
, và vân vân. Tôi sẽ cho bạn khám phá những điều đó.
Bạn cũng có thể đặt chủ của mình thành local[2]
trong Spark shell và trong các thử nghiệm của mình để xác định bất kỳ vấn đề nào chỉ có thể phát sinh khi bạn bắt đầu phân phối công việc.
Kiểm tra tích hợp với Spark
Bây giờ cho các công cụ thú vị.
Để kiểm tra tích hợp Spark sau khi bạn cảm thấy tin tưởng vào chất lượng của các chức năng trợ giúp và RDD
/ DataFrame
logic chuyển đổi của mình, điều quan trọng là phải thực hiện một số điều (bất kể công cụ xây dựng và khung kiểm tra):
- Tăng bộ nhớ JVM.
- Bật phân tách nhưng vô hiệu hóa thực thi song song.
- Sử dụng khung thử nghiệm của bạn để tích lũy các bài kiểm tra tích hợp Spark của bạn thành các bộ và khởi tạo
SparkContext
trước tất cả các bài kiểm tra và dừng nó sau tất cả các bài kiểm tra.
Với ScalaTest, bạn có thể trộn vào BeforeAndAfterAll
(mà tôi thường thích hơn) hoặc BeforeAndAfterEach
như @ShankarKoirala làm để khởi tạo và chia nhỏ các tạo tác Spark. Tôi biết đây là một nơi hợp lý để tạo một ngoại lệ, nhưng tôi thực sự không thích những thứ có thể thay đổi var
mà bạn phải sử dụng.
Mô hình cho vay
Một cách tiếp cận khác là sử dụng Mô hình Khoản vay .
Ví dụ (sử dụng ScalaTest):
class MySpec extends WordSpec with Matchers with SparkContextSetup {
"My analytics" should {
"calculate the right thing" in withSparkContext { (sparkContext) =>
val data = Seq(...)
val rdd = sparkContext.parallelize(data)
val total = rdd.map(...).filter(...).map(...).reduce(_ + _)
total shouldBe 1000
}
}
}
trait SparkContextSetup {
def withSparkContext(testMethod: (SparkContext) => Any) {
val conf = new SparkConf()
.setMaster("local")
.setAppName("Spark test")
val sparkContext = new SparkContext(conf)
try {
testMethod(sparkContext)
}
finally sparkContext.stop()
}
}
Như bạn có thể thấy, Mô hình Khoản vay sử dụng các chức năng bậc cao hơn để "cho mượn" SparkContext
bài kiểm tra và sau đó loại bỏ nó sau khi hoàn thành.
Lập trình định hướng đau khổ (Cảm ơn, Nathan)
Đó hoàn toàn là vấn đề sở thích, nhưng tôi thích sử dụng Mô hình Khoản vay và tự sắp xếp mọi thứ miễn là có thể trước khi đưa vào một khuôn khổ khác. Bên cạnh việc cố gắng duy trì dung lượng nhẹ, các khung công tác đôi khi thêm rất nhiều "phép thuật" khiến cho việc gỡ lỗi kiểm tra thất bại trở nên khó giải thích. Vì vậy, tôi tham gia chương trình Định hướng đau khổ phương pháp - nơi tôi tránh thêm một khung công tác mới cho đến khi nỗi đau không có nó là quá sức chịu đựng. Nhưng một lần nữa, điều này là vào bạn.
Sự lựa chọn tốt nhất cho khung thay thế đó tất nhiên là cơ sở thử nghiệm tia lửa như @ShankarKoirala đã đề cập. Trong trường hợp đó, kiểm tra ở trên sẽ giống như sau:
class MySpec extends WordSpec with Matchers with SharedSparkContext {
"My analytics" should {
"calculate the right thing" in {
val data = Seq(...)
val rdd = sc.parallelize(data)
val total = rdd.map(...).filter(...).map(...).reduce(_ + _)
total shouldBe 1000
}
}
}
Lưu ý rằng tôi không phải làm gì để đối phó với SparkContext
. SharedSparkContext
đã cho tôi tất cả những gì - với sc
nhưSparkContext
miễn phí. Cá nhân tôi mặc dù tôi sẽ không mang lại sự phụ thuộc này chỉ cho mục đích này vì Mô hình Khoản vay thực hiện chính xác những gì tôi cần cho điều đó. Ngoài ra, với rất nhiều điều không thể đoán trước xảy ra với các hệ thống phân tán, có thể là một nỗi đau thực sự khi phải truy tìm phép thuật xảy ra trong mã nguồn của thư viện bên thứ ba khi mọi thứ xảy ra sai trong quá trình tích hợp liên tục.
Giờ đây, nơi cơ sở thử nghiệm tia lửa thực sự tỏa sáng là với những người trợ giúp dựa trên Hadoop như HDFSClusterLike
vàYARNClusterLike
. Kết hợp những đặc điểm đó thực sự có thể giúp bạn tiết kiệm rất nhiều công sức khi thiết lập. Một nơi khác mà nó tỏa sáng là với các thuộc tính và trình tạo giống như Scalacheck - tất nhiên giả sử bạn hiểu cách kiểm tra dựa trên thuộc tính hoạt động và tại sao nó lại hữu ích. Nhưng một lần nữa, cá nhân tôi sẽ ngừng sử dụng nó cho đến khi các phép phân tích và thử nghiệm của tôi đạt đến mức độ tinh vi đó.
"Chỉ có một người Sith giải quyết sự tuyệt đối." - Obi-Wan Kenobi
Tất nhiên, bạn không cần phải chọn cái này hay cái kia. Có lẽ bạn có thể sử dụng phương pháp Mô hình Khoản vay cho hầu hết các thử nghiệm của mình và cơ sở thử nghiệm tia lửa chỉ cho một số thử nghiệm nghiêm ngặt hơn. Sự lựa chọn không phải là nhị phân; bạn có thể làm cả hai.
Kiểm tra tích hợp với Spark Streaming
Cuối cùng, tôi chỉ muốn trình bày một đoạn mã về thiết lập kiểm tra tích hợp SparkStreaming với các giá trị trong bộ nhớ có thể trông như thế nào mà không có cơ sở kiểm tra tia lửa :
val sparkContext: SparkContext = ...
val data: Seq[(String, String)] = Seq(("a", "1"), ("b", "2"), ("c", "3"))
val rdd: RDD[(String, String)] = sparkContext.parallelize(data)
val strings: mutable.Queue[RDD[(String, String)]] = mutable.Queue.empty[RDD[(String, String)]]
val streamingContext = new StreamingContext(sparkContext, Seconds(1))
val dStream: InputDStream = streamingContext.queueStream(strings)
strings += rdd
Điều này đơn giản hơn vẻ ngoài của nó. Nó thực sự chỉ biến một chuỗi dữ liệu thành một hàng đợi để cấp dữ liệu cho DStream
. Hầu hết nó thực sự chỉ là thiết lập bảng soạn sẵn hoạt động với các API Spark. Bất kể, bạn có thể so sánh điều này với StreamingSuiteBase
như được tìm thấy trong cơ sở thử nghiệm tia lửa để quyết định bạn thích cái nào hơn.
Đây có thể là bài viết dài nhất của tôi từ trước đến nay, vì vậy tôi sẽ để nó ở đây. Tôi hy vọng những người khác đồng tình với những ý tưởng khác để giúp cải thiện chất lượng phân tích của chúng tôi với cùng các phương pháp kỹ thuật phần mềm linh hoạt đã cải thiện tất cả các phát triển ứng dụng khác.
Và với những lời xin lỗi vì cái phích cắm vô liêm sỉ, bạn có thể xem khóa học Analytics của chúng tôi với Apache Spark , nơi chúng tôi giải quyết rất nhiều ý tưởng này và hơn thế nữa. Chúng tôi hy vọng sẽ sớm có phiên bản trực tuyến.