目錄標題
高階函數
1.值的函數
(1)使用場景
函數可以向數字,字符串一樣,可以將函數傳遞給一個方法
(2)示例
package com.day04
object FuncDemo {
def main(args: Array[String]): Unit = {
//1.創建函數,將數字轉換爲小星星
val function: Int => String = (num:Int) => "*" * num
//2.創建列表,執行轉換
val resultList = (1 to 10).map(function)
//3.打印測試
println(resultList)
}
}
2.匿名函數
不給函數賦值給變量,沒有賦值給變量的函數就是匿名函數
package com.day04
object FuncDemo {
def main(args: Array[String]): Unit = {
//1.創建函數,將數字轉換爲小星星
val startList = (1 to 10).map( "*"* _)
println(startList)
}
}
3.柯里化
是指將原先接受多個參數的方法轉換隻有一個參數的多個列表的過程
def func(x,y,z) 柯里化=> def func(x)(y)(z) 調用時,由左至右依次調用
原理是首先調用x,傳入x的函數的返回值,在被調用傳入y,傳入y的函數的返回值,在被調用傳入z
package com.day04
object FuncDemo {
//1.定義一個方法,計算兩個int類型的值
def calculate(a: Int, b: Int)(calc: (Int, Int) => Int) = {
calc(a, b)
}
def main(args: Array[String]): Unit = {
println(calculate(2, 3)(_ + _))
println(calculate(1, 3)(_ * _))
println(calculate(2, 3)(_ % _))
}
}
4.閉包
閉包就是一個函數的返回值依賴於聲明在函數外部的變量
package com.day04
object FuncDemo {
def main(args: Array[String]): Unit = {
//定義一個函數,訪問函數作用域外部的變量,產生閉包
val y =10
val addFunc: Int => Int = (x:Int) => x+y
println(addFunc(1))
}
}
隱式轉換
是指以implicit關鍵字聲明的帶有單個參數的方法,他說自動被調用的,自動將某種類型轉換爲另一種類型
1.使用步驟
- 在object中定義隱式轉換方法(使用implicit)
- 在需要用到隱式轉換的地方,引入隱式轉換(使用import)
- 自動調用隱式轉換後的方法
2.示例
package com.day04
import java.io.File
import scala.io.Source
object test {
class RichFile(val file:File){
def read() = {
//將文件內容讀取爲字符串
Source.fromFile(file).mkString
}
}
//創建隱式轉換
object ImpliciyDemo{
//將File對象轉換爲RichFile對象
implicit def fileToRichFile(file: File) = new RichFile(file)
}
def main(args: Array[String]): Unit = {
val file = new File("./Data/1.txt")
import ImpliciyDemo.fileToRichFile
//調用隱式轉換
println(file.read())
}
}
隱式參數
1.定義
- 在方法後面添加一個參數列表,參數使用implicit修飾
- 在object中定義implicit修飾的隱式值
- 調用方法,可以不傳入implicit修飾的參數列,編譯器會自動查找缺省值
2.示例
package com.day04
object test {
//1.定義一個方法,這個方法有一個隱式參數
def quote(what:String)(implicit delimeters:(String,String)): String = {
delimeters._1 + what + delimeters._2
}
//2.定義一個隱式參數
object ImplicitDemo{
implicit val delimeterParam = ("<<",">>") //修飾一個變量
}
//3.調用方法執行測試
def main(args: Array[String]): Unit = {
import ImplicitDemo.delimeterParam
println(quote("你好"))
}
}
Akka併發編程框架
1.特性,編程流程
(1)特性
- 提供基於異步非阻塞,高性能的事件驅動編程模型
- 內置容錯機制,潤興Actor出錯是,進行重置
- 使用Akka可以單機上構建高併發程序,也可以在網絡中構建分佈式程序
(2)Akka Actor的併發編程模型的基本流程
- 學生創建一個ActorSystem
- 通過ActorSystem來創建一個ActorRef(老師的引用),並將消息發送給ActorRef
- ActorRef將消息發送給Message Dispatcher(消息分發器)
- Message Dispatcher將消息按照順序保存到目標Actor的MailBox中
- Message Dispatcher將MailBox放到一個線程中
- MailBox按照順序取出消息,最終將它遞給TeacherActor接受的方法中
2.入門案例
(1)創建maven模塊
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<encoding>UTF-8</encoding>
<scala.version>2.11.8</scala.version>
<scala.compat.version>2.11</scala.compat.version>
</properties>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.3.14</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-remote_2.11</artifactId>
<version>2.3.14</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>reference.conf</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass></mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
(2)創建並加載Actor
創建兩個Actor
- SenderActor:用來發消息
- ReceiveActor:用來接收,回覆消息
發現消息: 接收類對象 ! 發送內容
(2).1創建Actor
- 創建ActorSystem
- 創建自定義Actor
- ActorSystem加載Actor
(2).2創建SenderActor單例類發送消息
package com.yuge.akk.demo
import akka.actor.Actor
//實現akk的Actor
object SenderActor extends Actor{
//在Actor併發模型中,需要實現的方法是act
//想要持續接收消息使用的是loop+react
//在akk編程模型中,直接在receive方法中寫一個偏函數就可以持續接收消息了
override def receive: Receive = {
case "start" => {
println("SenderActor:接收start消息")
//發送SubmitTaskMessage消息給ReceiveActor
//akka://actorSystem的名字/user/actor名字
val receiverActor = context.actorSelection("akka://actorSystem/user/receiverActor")
receiverActor ! SubmitTaskMessage("提交任務")
}
case SuccessSubmitTaskMessage(message) => println("SenderActor:收到任務成功提交消息")
}
}
(2).3創建ReceiveActor單例類接收消息
package com.yuge.akk.demo
import akka.actor.Actor
object ReceiveActor extends Actor{
override def receive: Receive = {
case SubmitTaskMessage(message) => {
println("ReceiverActor接收到任務提交消息")
//回覆消息
sender ! SuccessSubmitTaskMessage("接收成功,感謝")
}
}
}
(2).4實現程序入口Entrance
package com.yuge.akk.demo
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object Entrance {
def main(args: Array[String]): Unit = {
//2.創建ActorSystem
val actorSystem = ActorSystem("actorSystem", ConfigFactory.load()) //調用方法會自動加載resources裏面的配置文件
//3.加載Actor
val senderActor = actorSystem.actorOf(Props(SenderActor),"senderActor")
val receiverActor = actorSystem.actorOf(Props(ReceiveActor),"receiverActor")
//在main方法中,發送一個"start"字符串給SenderActor
senderActor ! "start"
}
}
(2).5創建MessageDifinition類,用於樣例類來封裝消息
package com.yuge.akk.demo
//提交任務消息
case class SubmitTaskMessage(message:String)
//任務提交成功消息
case class SuccessSubmitTaskMessage(message:String)
3.Akka定時任務
(1)使用場景
使用Akka框架定時的處理一些任務
(2)使用方式
Akka中,提供一個schedular對象來實現定時調度功能.使用ActorSystem.scheduler.schedule方法,可以啓動一個定時任務
schedule方法針對scala提供兩種使用形式
1.發送消息
def schedule(
initialDelay: FiniteDuration, // 延遲多久後啓動定時任務
interval: FiniteDuration, // 每隔多久執行一次
receiver: ActorRef, // 給哪個Actor發送消息
message: Any) // 要發送的消息類型
(implicit executor: ExecutionContext) // 隱式參數:需要手動導入
2.自定義實現
def schedule(
initialDelay: FiniteDuration, // 延遲多久後啓動定時任務
interval: FiniteDuration // 每隔多久執行一次
)(f: ⇒ Unit) // 定期要執行的函數,可以將邏輯寫在這裏
(implicit executor: ExecutionContext) // 隱式參數:需要手動導入
(3)示例-發送消息
package com.yuge.akk.scheduler
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object Entrance {
//1.創建一個Actor,接收打印消息
object ReceiverActor extends Actor{
override def receive: Receive = {
case x => println(x)
}
}
def main(args: Array[String]): Unit = {
//2.構建ActorSystem,加載Actor
val actorSystem = ActorSystem("actorSystem",ConfigFactory.load())
//加載Actor
val receiverActor: ActorRef = actorSystem.actorOf(Props(ReceiverActor),"receiverActor")
//導入隱式轉換
import scala.concurrent.duration._ //方便我們引用時間
//導入隱式參數
import actorSystem.dispatcher //執行上下文
//3.定時發送消息給Actor
//3.1延遲多久
//3.2定時任務週期
//3.3發送消息給那個Actor
//4.4發送消息的內容
// 隱式轉換:0這樣的整形就會多出來seconds方法指定時間,
actorSystem.scheduler.schedule(0.seconds,1 seconds,receiverActor,"hello")
}
}
(4)示例-自定義實現
package com.yuge.akk.scheduler
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object Entrance {
//1.創建一個Actor,接收打印消息
object ReceiverActor extends Actor{
override def receive: Receive = {
case x => println(x)
}
}
def main(args: Array[String]): Unit = {
//2.構建ActorSystem,加載Actor
val actorSystem = ActorSystem("actorSystem",ConfigFactory.load())
//加載Actor
val receiverActor: ActorRef = actorSystem.actorOf(Props(ReceiverActor),"receiverActor")
//導入隱式轉換
import scala.concurrent.duration._ //scala下的併發包中的duration,方便我們引用時間
//導入隱式參數
import actorSystem.dispatcher //執行上下文
///3.定時發送消息給Actor
//3.1延遲多久
// 隱式轉換:0這樣的整形就會多出來seconds方法指定時間,
actorSystem.scheduler.schedule(0 seconds,1 seconds){
//業務邏輯
receiverActor ! "hello"
}
}
}
(5)注意事項
- 需要導入隱式轉換import scala.concurrent.duration._才能調用0 seconds方法
- 需要導入隱式參數 import actorSystem.dispatcher 才能啓動定時任務
4.Akka進程間通信
(1)WorkerActor進行網絡配置
在resources創建名稱爲application.conf文件寫入以下內容
akka.actor.provider = "akka.remote.RemoteActorRefProvider" //支持遠程通信
akka.remote.netty.tcp.hostname="127.0.0.1" //IP地址
akka.remote.netty.tcp.port="9999" //端口號
(2)創建單例對象WorkActor用來接收消息
package com.yuge.akka
import akka.actor.Actor
object WorkerActor extends Actor{
override def receive: Receive = {
//編寫業務邏輯
case x =>println(x)
}
}
(3)創建Entrance入口
在這裏插入代碼片
5.簡易版spark通信框架案例
- 一個Master管理Worker
- 若干個Work
- 註冊
- 發送心跳
(1)實現思路
本案例分爲三個階段來實現:
- Worker註冊階段
- Worker進程向Master註冊(將自己的ID、CPU核數、內存大小(M)發送給Master)
- Worker定時發送心跳階段
Worker定期向Master發送心跳消息 - Master定時心跳檢測階段
Master定期檢查Worker心跳,將一些超時的Worker移除,並對Worker按照內存進行倒序排序 - 多個Worker測試階段
抽取Worker參數,通過命令行參數接收Worker參數(綁定端口號、CPU、內存)
(2)工程搭建
1.項目分塊
工程名 | 說明 |
---|---|
spark-demo-common | 存放公共消息,實體類 |
spark-demo-master | Akka Master節點 |
spark-demo-worker | Akka Work節點 |
2.maven依賴(藉助上面的maven)
//增加一個這個引用一下common模塊maven
<dependency>
<groupId>com.ityuge</groupId>
<artifactId>spark-demo-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
3.導入配置文件application.conf(在同一臺機器上不能有端口衝突)
- 修改Master的端口爲7000
- 修改Worker的端口爲7100
akka.actor.provider = "akka.remote.RemoteActorRefProvider" //支持遠程通信
akka.remote.netty.tcp.hostname="127.0.0.1" //IP地址
akka.remote.netty.tcp.port="7000" //端口號
akka.actor.provider = "akka.remote.RemoteActorRefProvider" //支持遠程通信
akka.remote.netty.tcp.hostname="127.0.0.1" //IP地址
akka.remote.netty.tcp.port="7100" //端口號
(3)構建Master和Work
1.步驟
- 創建並加載Master Actor
- 創建並加載Worker Actor
- 測試是否啓動成功