日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

java akka_Akka系列(九):Akka分布式之Akka Remote

發(fā)布時(shí)間:2024/9/19 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java akka_Akka系列(九):Akka分布式之Akka Remote 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Akka作為一個(gè)天生用于構(gòu)建分布式應(yīng)用的工具,當(dāng)然提供了用于分布式組件即Akka Remote,那么我們就來看看如何用Akka Remote以及Akka Serialization來構(gòu)建分布式應(yīng)用。

背景

很多同學(xué)在程序的開發(fā)中都會(huì)遇到一個(gè)問題,當(dāng)業(yè)務(wù)需求變得越來越復(fù)雜,單機(jī)服務(wù)器已經(jīng)不足以承載相應(yīng)的請(qǐng)求的時(shí)候,我們都會(huì)考慮將服務(wù)部署到不同的服務(wù)器上,但服務(wù)器之間可能需要相互調(diào)用,那么系統(tǒng)必須擁有相互通信的接口,用于相應(yīng)的數(shù)據(jù)交互,這時(shí)候一個(gè)好的遠(yuǎn)程調(diào)用方案是一個(gè)絕對(duì)的利器,主流的遠(yuǎn)程通信有以下幾種選擇:

RPC(Remote Procedure Call Protocol)

Web Service

RMI (Remote Method Invocation)

JMS(Java Messaging Service)

這幾種方式都是被采用比較廣泛的通信方案,有興趣的同學(xué)可以自己去了解一下,這里我會(huì)講一下RMI和JMS。

JAVA遠(yuǎn)程調(diào)用

RMI和JMS相信很多寫過Java程序的同學(xué)都知道,是Java程序用來遠(yuǎn)程通信的主要方式,那么RMI和JMS又有什么區(qū)別呢?

1.RMI

i.特征:

同步通信:在使用RMI調(diào)用遠(yuǎn)程方法時(shí),線程會(huì)持續(xù)等待直到結(jié)果返回,所以它是一個(gè)同步阻塞操作;

強(qiáng)耦合:請(qǐng)求的系統(tǒng)中需要使用的RMI服務(wù)進(jìn)行接口聲明,返回的數(shù)據(jù)類型有一定的約束;

ii.優(yōu)點(diǎn):

實(shí)現(xiàn)相對(duì)簡(jiǎn)單,方法調(diào)用形式通俗易理解,接口聲明服務(wù)功能清晰。

iii.缺點(diǎn):

只局限支持JVM平臺(tái);

對(duì)無法兼容Java語(yǔ)言的其他語(yǔ)言也不適用;

2.JMS

i.特征:

異步通信:JMS發(fā)送消息進(jìn)行通信,在通信過程中,線程不會(huì)被阻塞,不必等待請(qǐng)求回應(yīng),所以是一個(gè)異步操作;

松耦合:不需要接口聲明,返回的數(shù)據(jù)類型可以是各種各樣,比如JSON,XML等;

ii.通信方式:

(1)點(diǎn)對(duì)點(diǎn)消息傳送模型

顧名思義,點(diǎn)對(duì)點(diǎn)可以理解為兩個(gè)服務(wù)器的定點(diǎn)通信,發(fā)送者和接收者都能明確知道對(duì)方是誰(shuí),大致模型如下:

(2)發(fā)布/訂閱消息傳遞模型

點(diǎn)對(duì)點(diǎn)模型有些場(chǎng)景并不是很適用,比如有一臺(tái)主服務(wù)器,它產(chǎn)生一條消息需要讓所有的從服務(wù)器都能收到,若采用點(diǎn)對(duì)點(diǎn)模型的話,那主服務(wù)器需要循環(huán)發(fā)送消息,后續(xù)若有新的從服務(wù)器增加,還要改主服務(wù)器的配置,這樣就會(huì)導(dǎo)致不必要的麻煩,那么發(fā)布/訂閱模型是怎么樣的呢?其實(shí)這種模式跟設(shè)計(jì)模式中的觀察者模式很相似,相信很多同學(xué)都很熟悉,它最大的特點(diǎn)就是較松耦合,易擴(kuò)展等特點(diǎn),所以發(fā)布/訂閱模型的大致結(jié)構(gòu)如下:

iii.優(yōu)點(diǎn):

由于使用異步通信,不需要線程暫停等待,性能相對(duì)較高。

iiii.缺點(diǎn):

技術(shù)實(shí)現(xiàn)相對(duì)復(fù)雜,并需要維護(hù)相關(guān)的消息隊(duì)列;

更通俗的說:

RMI可以看成是用打電話的方式進(jìn)行信息交流,而JMS更像是發(fā)短信。

總的來說兩種方式?jīng)]有孰優(yōu)孰劣,我們也不用比較到底哪種方式比較好,存在即合理,更重要的是哪種選擇可能更適合你的系統(tǒng)。

Akka Remote

上面講到JAVA中遠(yuǎn)程通信的方式,但我們之前說過Akka也是基于JVM平臺(tái)的,那么它的通信方式又有什么不同呢?

在我看來,Akka的遠(yuǎn)程通信方式更像是RMI和JMS的結(jié)合,但更偏向于JMS的方式,為什么這么說呢,我們先來看一個(gè)示例:

我們先來創(chuàng)建一個(gè)遠(yuǎn)程的Actor:

class RemoteActor extends Actor {

def receive = {

case msg: String =>

println(s"RemoteActor received message '$msg'")

sender ! "Hello from the RemoteActor"

}

}

現(xiàn)在我們?cè)谶h(yuǎn)程服務(wù)器上啟動(dòng)這個(gè)Actor:

val system = ActorSystem("RemoteDemoSystem")

val remoteActor = system.actorOf(Props[RemoteActor], name = "RemoteActor")

那么現(xiàn)在我們假如有一個(gè)系統(tǒng)需要向這個(gè)Actor發(fā)送消息應(yīng)該怎么做呢?

首先我們需要類似RMI發(fā)布自己的服務(wù)一樣,我們需要為其他系統(tǒng)調(diào)用遠(yuǎn)程Actor提供消息通信的接口,在Akka中,設(shè)置非常簡(jiǎn)單,不需要代碼侵入,只需簡(jiǎn)單的在配置文件里配置即可:

akka {

actor {

provider = "akka.remote.RemoteActorRefProvider"

}

remote {

enabled-transports = ["akka.remote.netty.tcp"]

netty.tcp {

hostname = $localIp //比如127.0.0.1

port = $port //比如2552

}

log-sent-messages = on

log-received-messages = on

}

}

我們只需配置相應(yīng)的驅(qū)動(dòng),傳輸方式,ip,端口等屬性就可簡(jiǎn)單完成Akka Remote的配置。

當(dāng)然本地服務(wù)器也需要配置這些信息,因?yàn)锳kka之間是需要相互通信的,當(dāng)然配置除了hostname有一定的區(qū)別外,其他配置信息可一致,本例子是在同一臺(tái)機(jī)器上,所以這里hostname是相同的。

這時(shí)候我們就可以在本地的服務(wù)器向這個(gè)Actor發(fā)送消息了,首先我們可以創(chuàng)建一個(gè)本地的Actor:

case object Init

case object SendNoReturn

class LocalActor extends Actor{

val path = ConfigFactory.defaultApplication().getString("remote.actor.name.test")

implicit val timeout = Timeout(4.seconds)

val remoteActor = context.actorSelection(path)

def receive: Receive = {

case Init => "init local actor"

case SendNoReturn => remoteActor ! "hello remote actor"

}

}

其中的remote.actor.name.test的值為:“akka.tcp://RemoteDemoSystem@127.0.0.1:4444/user/RemoteActor”,另外我們可以看到我們使用了context.actorSelection(path)來獲取的是一個(gè)ActorSelection對(duì)象,若是需要獲得ActorRef,我們可以調(diào)用它的resolveOne(),它返回的是是一個(gè)Future[ActorRef],這里是不是很熟悉,因?yàn)樗镜孬@取Actor方式是一樣的,因?yàn)锳kka中Actor是位置透明的,獲取本地Actor和遠(yuǎn)程Actor是一樣的。

最后我們首先啟動(dòng)遠(yuǎn)程Actor的系統(tǒng):

object RemoteDemo extends App {

val system = ActorSystem("RemoteDemoSystem")

val remoteActor = system.actorOf(Props[RemoteActor], name = "RemoteActor")

remoteActor ! "The RemoteActor is alive"

}

然后我們?cè)诒镜叵到y(tǒng)中啟動(dòng)這個(gè)LocalActor,并向它發(fā)送消息:

object LocalDemo extends App {

implicit val system = ActorSystem("LocalDemoSystem")

val localActor = system.actorOf(Props[LocalActor], name = "LocalActor")

localActor ! Init

localActor ! SendNoReturn

}

我們可以看到RemoteActor收到了一條消息:

從以上的步驟和結(jié)果看出可以看出,Akka的遠(yuǎn)程通信跟JMS的點(diǎn)對(duì)點(diǎn)模式似乎更相似一點(diǎn),但是它有不需要我們維護(hù)消息隊(duì)列,而是使用Actor自身的郵箱,另外我們利用context.actorSelection獲取的ActorRef,可以看成遠(yuǎn)程Actor的副本,這個(gè)又和RMI相關(guān)概念類似,所以說Akka遠(yuǎn)程通信的形式上像是RMI和JMS的結(jié)合,當(dāng)然底層還是通過TCP、UDP等相關(guān)網(wǎng)絡(luò)協(xié)議進(jìn)行數(shù)據(jù)傳輸?shù)?#xff0c;從配置文件的相應(yīng)內(nèi)容便可以看出。

上述例子演示的是sendNoReturn的模式,那么假如我們需要遠(yuǎn)程Actor給我們一個(gè)回復(fù)應(yīng)該怎么做呢?

首先我們創(chuàng)建一個(gè)消息:

case object SendHasReturn

def receive: Receive = {

case SendHasReturn =>

for {

r

} yield r

}

我們重新運(yùn)行LocalActor并像RemoteActor發(fā)送一條消息:

可以看到LocalActor在發(fā)送消息后并收到了RemoteActor返回來的消息,另外我們這里設(shè)置了超時(shí)時(shí)間,若在規(guī)定的時(shí)間內(nèi)沒有得到反饋,程序就會(huì)報(bào)錯(cuò)。

Akka Serialization

其實(shí)這一部分本可以單獨(dú)拿出來寫,但是相信序列化這塊大家都應(yīng)該有所了解了,所以就不準(zhǔn)備講太多序列化的知識(shí)了,怕班門弄斧,主要講講Akka中的序列化。

繼續(xù)上面的例子,假如我們這時(shí)向RemoteActor發(fā)送一個(gè)自定義的對(duì)象,比如一個(gè)case class對(duì)象,但是我們這是是在網(wǎng)絡(luò)中傳輸這個(gè)消息,那么怎么保證這個(gè)對(duì)象類型和值呢,在同一個(gè)JVM系統(tǒng)中我們不需要擔(dān)心這個(gè),因?yàn)閷?duì)象就在堆中,我們只要傳遞相應(yīng)的地址即可就行,但是在不同的環(huán)境中,我們并不能這么做,我們?cè)诰W(wǎng)絡(luò)中只能傳輸字節(jié)數(shù)據(jù),所以我們必須將對(duì)象做特殊的處理,在傳輸?shù)臅r(shí)候轉(zhuǎn)化成特定的由一連串字節(jié)組成的數(shù)據(jù),而且我們又可以根據(jù)這些數(shù)據(jù)恢復(fù)成一個(gè)相應(yīng)的對(duì)象,這便是序列化。

我們先定義一個(gè)參與的case class, 并修改一下上面發(fā)送消息的語(yǔ)句:

case object SendSerialization

case class JoinEvt(

id: Long,

name: String

)

def receive: Receive = {

case SendSerialization =>

for {

r

} yield println(r)

}

這時(shí)我們重新啟動(dòng)RemoteActor和LocalActor所在的系統(tǒng),發(fā)送這條消息:

有同學(xué)可能會(huì)覺得奇怪,我們明明沒有對(duì)JoinEvt進(jìn)行過任何序列化的標(biāo)識(shí)和處理,為什么程序還能運(yùn)行成功呢?

其實(shí)不然,只不過是有人替我們默認(rèn)做了,不用說,肯定是貼心的Akka,它為我們提供了一個(gè)默認(rèn)的序列化策略,那就是我們熟悉又糾結(jié)的java.io.Serializable,沉浸在它的易使用性上,又對(duì)它的性能深惡痛絕,尤其是當(dāng)有大量對(duì)象需要傳輸?shù)姆植际较到y(tǒng),如果是小系統(tǒng),當(dāng)我沒說,畢竟存在即合理。

又有同學(xué)說,既然Akka是一個(gè)天生分布式組件,為什么還用低效的java.io.Serializable,你問我我也不知道,可能當(dāng)時(shí)的作者偷了偷懶,當(dāng)然Akka現(xiàn)在可能覺醒了,首先它支持第三方的序列化工具,當(dāng)然如果你有特殊需求,你也可以自己實(shí)現(xiàn)一個(gè),而且在最新的文檔中說明,在Akka 2.5x之后Akka內(nèi)核消息全面廢棄java.io.Serializable,用戶自定義的消息暫時(shí)還是支持使用java.io.Serializable的,但是不推薦用,因?yàn)樗堑托У?#xff0c;容易被攻擊,所以在這里我也推薦大家再Akka中盡量不要在使用了java.io.Serializable。

那么在Akka中我們?nèi)绾问褂玫谌降男蛄谢ぞ吣?#xff1f;

這里我推薦一個(gè)在Java社區(qū)已經(jīng)久負(fù)盛名的序列化工具:kryo,有興趣的同學(xué)可以去了解一下:kryo,而且它也提供Akka使用的相關(guān)包,這里我們就使用它作為示例:

這里我貼上整個(gè)項(xiàng)目的build.sbt, kryo的相關(guān)依賴也在里面:

import sbt._

import sbt.Keys._

lazy val AllLibraryDependencies =

Seq(

"com.typesafe.akka" %% "akka-actor" % "2.5.3",

"com.typesafe.akka" %% "akka-remote" % "2.5.3",

"com.twitter" %% "chill-akka" % "0.8.4"

)

lazy val commonSettings = Seq(

name := "AkkaRemoting",

version := "1.0",

scalaVersion := "2.11.11",

libraryDependencies := AllLibraryDependencies

)

lazy val remote = (project in file("remote"))

.settings(commonSettings: _*)

.settings(

// other settings

)

lazy val local = (project in file("local"))

.settings(commonSettings: _*)

.settings(

// other settings

)

然后我們只需將application.conf中的actor配置替換成以下的內(nèi)容:

actor {

provider = "akka.remote.RemoteActorRefProvider"

serializers {

kryo = "com.twitter.chill.akka.AkkaSerializer"

}

serialization-bindings {

"java.io.Serializable" = none

"scala.Product" = kryo

}

}

其實(shí)其中的"java.io.Serializable" = none可以省略,因?yàn)槿羰怯衅渌蛄谢牟呗詣t會(huì)替換掉默認(rèn)的java.io.Serializable的策略,這里只是為了更加仔細(xì)的說明。

至此我們就可以使用kryo了,整個(gè)過程是不是很easy,迫不及待開始寫demo了,那就快快開始吧。

整個(gè)例子的相關(guān)的源碼已經(jīng)上傳到akka-demo中:源碼鏈接

總結(jié)

以上是生活随笔為你收集整理的java akka_Akka系列(九):Akka分布式之Akka Remote的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。