The purpose of cluster sharding is to provide a framework to facilitate DDD, although I have not yet figured out what the application of DDD is, but cluster sharding is a tool that I am currently working on to extend the project to the cluster.
Sharding to do such a few things.
1. For each entity, create an actor. The entity has an Id as the unique identifier. All messages for this entity are handled by this actor
2. When the actor does not work for a period of time, it will time out and kill self
3. When a new node is added to a cluster, the new actor will be automatically created on the new nodes, or the old actor will be load balanced and migrated to new node. Similarly, when a node is hung, the suspended actor is migrated to the new
Before sharding was introduced, there were a lot of people on Google Group and StackOverflow who wanted something like this, which was 2011, and it's been 2015 years.
Sharding's principle and usage doc has a more detailed description, the following is a small test:
The first is the definition of the entity actor:
Object Actionworker { def props (): props = props (new Actionworker) //must return S:command val idextractor:s Hardregion.idextractor = {case S:command = (s.id, s) } val shardResolver:ShardRegion.ShardResolver = m sg = MSG Match {case s:command = (math.abs (s.id.hashcode)%). ToString } Val Shardname:stri ng = "Actionworker"}
Idextractor extracts the ID from the message as the basis for the route, and the second item of the return value is the message that the actor received
Shardresolver based on the ID to determine where the Shard is located, the way the hash function is set I did not study, Doc said it is 10 times times the ID can be.
Class Actionworker extends Actor { val log = Logging (Context.system, this) println ("Action worker is created")
context.setreceivetimeout (seconds) override def receive:receive = {case Command (ID, payload) = = Val Selfdesc = self.path.parent.name + "-" + self.path.name println ("Here I am, working:" + selfdesc) log.info ("H ere I am, working: "+ selfdesc) case receivetimeout = Log.info (" Nothing to do, better kill myself ") Val Selfdesc = Self.path.parent.name + "-" + self.path.name println ("Here I am, working:" + selfdesc) println ("Noth ing to does, better kill myself ") context.stop (self) }
There is nothing special about the actor definition, and it is important to note that
1. The actor received the second item of Idextract, not s:command that thing.
2. Actors do not have a normal way to get their own ID, a workaroud way is to get their own ID through Self.path.name, and then complete some initialization operation
Worker processes data, the actor that generates the data is called a Bot
Class Bot extends Actor { val log = Logging (Context.system, this) val ticktask = Context.system.scheduler.schedul E (3.seconds, 10.seconds, Self, Command ("")) def receive = Create val postregion = clustersharding ( Context.system). Shardregion (Actionworker.shardname) val create:receive = {case Command (ID, payload) = = val PostID = Random.nextint (5). ToString log.info ("bot create new command and received:" + PostID) println (" New command PostID = "+ PostID) postregion! Command (PostID, PostID) }}
Bot generated data to the worker actor, pay attention to the Postregion method, it first from the Actorsystem to get Clustersharding collection (a actorsystem may have more than Shard), and then according to SH The ardname is positioned to the unique shard. Finally, the message sent to Postregin,postregin will be forwarded.
The StartUp function of the Main method
def startup (Ports:seq[string]): Unit = {ports foreach {port =//Override The configuration of the port val config = configfactory.parsestring ("akka.remote.netty.tcp.port=" + port). Withfallback (Configfactory.load ())//Create an Akka system val system = Actorsystem ("clustersystem", config) Startupsharedjournal (System, Startstore = (Port = = "2551"), Path = Actorpath.fromstring ("akka.tcp://[email Protected]:2551/user/store ")) clustersharding (System). Start (TypeName = Actionworker.shardname, ENTRYPR OPS = Some (Actionworker.props ()), Idextractor = actionworker.idextractor, Shardresolver = Actionworker.shard Resolver) if (Port! = "2551" && port! = "2552") system.actorof (Props[bot], "Bot")}} def startup Sharedjournal (System:actorsystem, Startstore:boolean, path:actorpath): Unit = {//Start the shared journal on one n Ode (Don ' t crash this SPOF)//This is not being needed WITH a distributed journal if (Startstore) system.actorof (Props[sharedleveldbstore], "store")//Register the SH Ared Journal Import system.dispatcher implicit val timeout = timeout (15.seconds) val f = (System.actorselection (p ATH)? Identify (None)) f.onsuccess {case Actoridentity (_, Some (ref)) = = Sharedleveldbjournal.setstore (ref, System) Case _ = = System.log.error ("Shared journal not started at {}", path) System.shutdown ()} F.onfai Lure {Case _ = System.log.error ("Lookup of shared journal at {} timed out", path) System.shutdown ( ) } }
Startupsharedjournal is a must-do, otherwise cluster can't get away.
Clustersharding in this actorsystem start, the parameters are more intuitive.
It is important to note that:
1. Sharedjournal is used in the test case, in the PROD environment should be used journal
2. Cluster sharding is implemented with cluster singleton
Akka Cluster sharding