Advanced Akka For Architects
Transcript of Advanced Akka For Architects
Akka Multi-Data Center Clustering
Cluster Singletons
Cluster Sharding
Persistence
Akka Single Node
Akka Cluster
Single Data Center
Akka Cluster
Single Data Center
Split Brain
Unresponsive Member
Speed of light 300 km/ms, 186 mi/ms Small circle radius 3,000 km, 1,860 mi ~ 10 ms SOL distance
Single Akka Cluster
Multi Data Center
Multiple Akka Clusters
Multi Data Center
Multiple Akka Clusters
Multi Data Center
akka.cluster { # Change this to real hostname for production seed-nodes = ["akka.tcp://ClusterSystem@host1:2552", "akka.tcp://ClusterSystem@host2:2552"]
# Change this to the Akka data center this node belongs to multi-data-center.self-data-center = DC-A}akka.persistence { snapshot-store.plugin = "cassandra-snapshot-store"
multi-data-center { all-data-centers = ["DC-A", “DC-B", “DC-C”] }}
Multiple Akka Clusters
Clusters within Clusters
Multiple Akka Clusters
Clusters within Clusters
Network Partition
Multiple Akka Clusters
Clusters within Clusters
Node Failure
Cluster Events
Within Data Center MemberJoined
MemberUp MemberExited
MemberRemoved UnreachableMember
ReachableMember
Cross Data Center UnreachableMember
ReachableMember
Multiple Akka Clusters
Physical or Logical “Data Centers”
Clusters within Clusters
Microservice
Microservice Micros
ervic
e
Cluster Singleton
One per Data Center
Cluster Sharding
One per Data Center
Cluster Sharding
Shard Region Proxy
Cluster Sharding
ShardRegion Proxy
val counterProxyDcB: ActorRef = ClusterSharding(system).startProxy( typeName = "Counter", role = None, dataCenter = Some(“C"), extractEntityId = extractEntityId, extractShardId = extractShardId)
Akka Persistence
One per Data Center
Multi Data Center Persistence
Commercial Add-on
Multi-DC Persistence
Replicated Entities
Multi-DC Persistence
Concurrent Writes
import akka.persistence.multidc.scaladsl.ReplicatedEntity
final class Post1 extends ReplicatedEntity[BlogCommand, BlogEvent, BlogState] {
override def initialState: BlogState = BlogState.empty
override def commandHandler: CommandHandler = CommandHandler { (ctx, state, cmd) => ??? }
override def eventHandler(state: BlogState, event: BlogEvent): BlogState = ???}
import akka.persistence.multidc.javadsl.CommandHandler;import akka.persistence.multidc.javadsl.EventHandler;import akka.persistence.multidc.javadsl.ReplicatedEntity;
final class Post1 extends ReplicatedEntity<BlogCommand, BlogEvent, BlogState> {
@Override public BlogState initialState() { return BlogState.EMPTY; }
@Override public CommandHandler<BlogCommand, BlogEvent, BlogState> commandHandler() { throw new RuntimeException("Not implemented yet"); }
@Override public EventHandler<BlogEvent, BlogState> eventHandler() { throw new RuntimeException("Not implemented yet"); }}
import akka.persistence.multidc.javadsl.CommandHandler;import akka.persistence.multidc.javadsl.EventHandler;import akka.persistence.multidc.javadsl.ReplicatedEntity;
final class Post1 extends ReplicatedEntity<BlogCommand, BlogEvent, BlogState> {
@Override public BlogState initialState() { return BlogState.EMPTY; }
@Override public CommandHandler<BlogCommand, BlogEvent, BlogState> commandHandler() { throw new RuntimeException("Not implemented yet"); }
@Override public EventHandler<BlogEvent, BlogState> eventHandler() { throw new RuntimeException("Not implemented yet"); }}
public CommandHandler<BlogCommand, BlogEvent, BlogState> commandHandler() { return commandHandlerBuilder(BlogCommand.class) .matchCommand(AddPost.class, (ctx, state, cmd) -> { final PostAdded evt = new PostAdded(cmd.postId, cmd.content, state.contentTimestamp.increase(currentTimeMillis(), getSelfDc())); return Effect().persist(evt).andThen((state2) -> // After persist is done additional side effects can be performed ctx.getSender().tell(new AddPostDone(cmd.postId), getSelf()) ); })
public EventHandler<BlogEvent, BlogState> eventHandler() { return eventHandlerBuilder(BlogEvent.class) .matchEvent(PostAdded.class, (state, postAdded) -> { if (postAdded.timestamp.isAfter(state.contentTimestamp)) { return state.withContent(postAdded.content, postAdded.timestamp); } else { return state; } })
Multi-DC Persistence
Replication and handling of events
Multi-DC Persistence - Last writer wins
// eventHandler is used both when persisting new events, replaying// events, and consuming replicated events.override def eventHandler(state: BlogState, event: BlogEvent): BlogState = { event match { case PostAdded(postId, content, timestamp) => if (timestamp.isAfter(state.contentTimestamp)) state.withContent(content, timestamp) else state
case BodyChanged(_, newContent, timestamp) => if (timestamp.isAfter(state.contentTimestamp)) state.withContent(newContent, timestamp) else state
case Published(_) => state.copy(published = true) }}
// the returned event handler is used both when persisting new events, replaying// events, and consuming replicated events.@Overridepublic EventHandler<BlogEvent, BlogState> eventHandler() { return eventHandlerBuilder(BlogEvent.class) .matchEvent(PostAdded.class, (state, postAdded) -> { if (postAdded.timestamp.isAfter(state.contentTimestamp)) { return state.withContent(postAdded.content, postAdded.timestamp); } else { return state; } }) .matchEvent(BodyChanged.class, (state, bodyChanged) -> { if (bodyChanged.timestamp.isAfter(state.contentTimestamp)) { return state.withContent(bodyChanged.content, bodyChanged.timestamp); } else { return state; } }) .matchEvent(Published.class, (state, publish) -> state.publish()) .matchAny((state, otherEvent) -> state);}
override def eventTrigger( ctx: EventTriggerContext, state: State, event: Event): Effect[Event, State] = { event match { case StepStarted(nr) => authority ! RequestApproval(nr) ctx.actorContext.timers.startPeriodicTimer(ResendTick, ResendTick, resendInterval) Effect.none case StepApproved(_, _) => if (!state.denied && selfDc == "DC-A" && state.isCurrentStepApproved && state.currentStep < maxSteps) { // approved by all, continue with next step log.info("Step {} approved by all, continue", state.currentStep) Effect.persist(StepStarted(state.currentStep + 1)) } else Effect.none case _ => Effect.none } }
@Override public Effect<Event, State> eventTrigger(EventTriggerContext ctx, State state, Event event) { if (event instanceof StepStarted) { StepStarted stepStarted = (StepStarted) event; authority.tell(new RequestApproval(stepStarted.stepNr), ctx.actorContext().getSelf()); ctx.actorContext().getTimers().startPeriodicTimer(ResendTick.INSTANCE, ResendTick.INSTANCE, resendInterval); return Effect().none(); } else if (event instanceof StepApproved) { StepApproved stepApproved = (StepApproved) event; if (!state.denied && getSelfDc().equals("DC-A") && state.isCurrentStepApproved() && state.currentStep < maxSteps) { // approved by all, continue with next step log().info("Step {} approved by all, continue", state.currentStep); return Effect().persist(new StepStarted(state.currentStep + 1)); } else return Effect().none();
} else { return Effect().none(); } }
Akka Multi-Data Center Clustering
In summary
Want to learn more about Akka Multi Data Center?
Cluster across multiple data centers - http://bit.ly/2iY8qLO
Multi-DC Persistence (Akka Commercial Addons) - http://bit.ly/2zSN9GC
Get your team hooked on Akka with this comprehensive introduction by Hugh McKee: “Akka Revealed”
GO TO VIDEO & SLIDES
Next Steps - Introduce Your Team To Akka
Learn why Akka is so ideal for distributed systems with a free copy of Hugh McKee’s O’Reilly eBook: Designing Reactive Systems: The Role of Actors in Distributed Architecture
GET A COPY
Next Steps - Learn The “Why” Of Akka Actors
Are you a serious enterprise looking to run Akka and other Lightbend technologies in production? Let us know when it’s time for a chat!
CONTACT US
Next Steps - Enterprise Add-Ons & Support