Given the following appliction.conf :
akka {
loglevel = debug
actor {
provider = cluster
serialization-bindings {
"sample.cluster.CborSerializable" = jackson-cbor
}
}
remote {
artery {
canonical.hostname = "127.0.0.1"
canonical.port = 0
}
}
cluster {
roles= ["testrole1" , "testrole2"]
seed-nodes = [
"akka://ClusterSystem#127.0.0.1:25251",
"akka://ClusterSystem#127.0.0.1:25252"]
downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider"
}
}
To discern between the roles within an Actor I use :
void register(Member member) {
if (member.hasRole("testrole1")) {
//start actor a1
}
else if (member.hasRole("testrole2")) {
//start actor a2
}
}
edited from src (https://doc.akka.io/docs/akka/current/cluster-usage.html)
To enable role for a node I use the following config :
Within application.conf I configure the array for the roles but this appears to be at the cluster level rather than node level. In other words it does not seem possible to configure application.conf such that the Akka cluster is instructed to start actor a1 on node n1 and actor a2 on node n2? Should note details be specified at the level of akka.cluster in application.conf ?
For each node, is it required to specify multiple application.conf configuration files?
For example, application.conf for testrole1
akka {
loglevel = debug
actor {
provider = cluster
serialization-bindings {
"sample.cluster.CborSerializable" = jackson-cbor
}
}
remote {
artery {
canonical.hostname = "127.0.0.1"
canonical.port = 0
}
}
cluster {
roles= ["testrole1"]
seed-nodes = [
"akka://ClusterSystem#127.0.0.1:25251",
"akka://ClusterSystem#127.0.0.1:25252"]
downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider"
}
}
application.conf for testrole2 :
akka {
loglevel = debug
actor {
provider = cluster
serialization-bindings {
"sample.cluster.CborSerializable" = jackson-cbor
}
}
remote {
artery {
canonical.hostname = "127.0.0.1"
canonical.port = 0
}
}
cluster {
roles= ["testrole2"]
seed-nodes = [
"akka://ClusterSystem#127.0.0.1:25251",
"akka://ClusterSystem#127.0.0.1:25252"]
downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider"
}
}
The difference between each application.conf defined above is the value of akka.cluster.roles is either "testrole1" or "testrole2".
How should application.conf be configured such that the Akka cluster is instructed to start actor a1 on node n1 and actor a2 on node n2? Should node details be specified at the level of akka.cluster in application.conf ?
Update:
Another option is to pass the rolename via an environment variable? I've just noticed this is explicitly stated here: https://doc.akka.io/docs/akka/current/typed/cluster.html "The node roles are defined in the configuration property named akka.cluster.roles and typically defined in the start script as a system property or environment variable." In this scenario, use the same application.conf file for all nodes but each node uses an environment variable. For example, an updated appliction.conf (note addition of "ENV_VARIABLE")
akka {
loglevel = debug
actor {
provider = cluster
serialization-bindings {
"sample.cluster.CborSerializable" = jackson-cbor
}
}
remote {
artery {
canonical.hostname = "127.0.0.1"
canonical.port = 0
}
}
cluster {
roles= ["ENV_VARIABLE"]
seed-nodes = [
"akka://ClusterSystem#127.0.0.1:25251",
"akka://ClusterSystem#127.0.0.1:25252"]
downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider"
}
}
Cluster startup scripts determine the role for each node via the ENV_VARIABLE parameter, is this a viable solution?
If you're going to assign different roles to different nodes, those nodes cannot use the same configuration. The easiest way to accomplish this is through n1 having "testRole1" in its akka.cluster.roles list and n2 having "testRole2" in its akka.cluster.roles list.
Everything in akka.cluster config is only configuring that node for participation in the cluster (it's configuring the cluster component on that node). A few of the settings have to be the same across the nodes of a cluster (e.g. the SBR settings), but a setting on n1 doesn't affect a setting on n2.
Related
I am trying to do clustering in CentOS 6.10
My versions of Jdk , JRE=
java version "1.8.0_60"
javac 1.8.0_60
This is my config.tdsl file for ip =192.168.4.109
admins = [
'admin#imrggn.com'
]
'config-type' = 'default'
debug = [ 'server' ]
'default-virtual-host' = 'imrggn.com'
dataSource () {
default () {
uri = 'jdbc:derby:tigasedb;create=true'
}
}
'audit-log' () {}
http () {
setup () {
'admin-password' = 'tigase'
'admin-user' = 'admin'
}
}
pubsub () {
trusted = [ 'http#{clusterNode}' ]
}
'sess-man' () {
'audit-log' () {}
}
'dns-resolver' {
-'tigase-resolver-class' = -'tigase.util.DNSResolverDefault'
-'tigase-primary-address' = -'192.168.4.109'
-'tigase-secondary-address' = -'192.168.4.109'
}
stun (class: tigase.stun.StunComponent) {
-'stun-primary-ip' = -'mc2.imrggn.com'
-'stun-primary-port' = 3478
-'stun-secondary-ip' = -'hey-sjain-l'
-'stun-secondary-port' = 7001
}
'cluster-mode' = true
'cluster-nodes' = [ -'mc2.imrggn.com', -'mc1.imrggn.com']
When I run Wireshark ie tcp.port==5277 , I see nothing there at all where the default port is 5277 only , hence it shows no value over there on neither of the machines
But lsof -iTCP:5277 shows java 21155 root 175u IPv6 733910 0t0 TCP *:5277 (LISTEN)
My ip = 192.168.4.109 hostname = mc2.imrggn.com
And for the other machine is My ip = 192.168.4.111 hostname = mc1.imrggn.com
What is wrong ?
The problem boils down to your database setup (`uri = 'jdbc:derby:tigasedb;create=true') - for the clustering to work (and make sense) you would have to use a database, that would be available to all cluster nodes (in this case MySQL, PostgreSQL, Microsoft SQLServer or MongoDB).
The reason is two-fold:
if you would use local DerbyDB on all nodes then all information would be local (i.e. you would have different list of users on each node
(more important) Tigase uses cluster auto-discover which utilizes the database (each node announces it's availability via shared database)
I am new to Kafka. Tried to implement consumer and producer classes to send and receive messages. Need to configure bootstrap.servers for both classes which is a list of broker's ip and port separated by ,. For example,
producerConfig.put("bootstrap.servers",
"172.16.20.207:9946,172.16.20.234:9125,172.16.20.36:9636");
Since the application will be running on the master node of a cluster, it should be able to retrieve the broker information from ZooKeeper just like the answer to Kafka: Get broker host from ZooKeeper.
public static void main(String[] args) throws Exception {
ZooKeeper zk = new ZooKeeper("localhost:2181", 10000, null);
List<String> ids = zk.getChildren("/brokers/ids", false);
for (String id : ids) {
String brokerInfo = new String(zk.getData("/brokers/ids/" + id, false, null));
System.out.println(id + ": " + brokerInfo);
}
}
However this brokerInfo is in Json format which looks like this:
{"jmx_port":-1,"timestamp":"1428512949385","host":"192.168.0.11","version":1,"port":9093}
In this same post, another one suggested the following way of getting connection string for each broker and join them together with comma.
for (String id : ids) {
String brokerInfoString = new String(zk.getData("/brokers/ids/" + id, false, null));
Broker broker = Broker.createBroker(Integer.valueOf(id), brokerInfoString);
if (broker != null) {
brokerList.add(broker.connectionString());
}
}
If this Broker class is from org.apache.kafka.common.requests.UpdateMetadataRequest.Broker, it does not have methods createBroker and connectionString.
Found another similar post Getting the list of Brokers Dynamically. But it did not say how to get the attribute from broker info such as host and port. I can probably write a parser for the json like string to extract them, but I suspect there is more Kafka native way to do that. Any suggestions?
EDIT: I realized the Broker class is from kafka.cluster.Broker. Still it does not have method connectionstring().
You could use ZkUtils to retrieve all the broker information in the cluster, as show below:
ZkUtils zk = ZkUtils.apply("zkHost:2181", 6000, 6000, true);
List<Broker> brokers = JavaConversions.seqAsJavaList(zk.getAllBrokersInCluster());
for (Broker broker : brokers) {
//assuming you do not enable security
System.out.println(broker.getBrokerEndPoint(SecurityProtocol.PLAINTEXT).host());
}
zk.close();
When configuring the Cluster Routing in the config file using:
akka.actor.deployment {
/jobDispatcher/singleton/workerRouter {
router = round-robin-pool
nr-of-instances = 5
cluster {
enabled = on
max-number-of-instances-per-node = 1
allow-local-routees = on
}
}
}
I can lookup a routed worker using:
ActorRef actor = context().actorOf( //
FromConfig.getInstance().props( //
Props.create(MyRoutedActor.class)), //
"workerRouter");
I would prefer configuring the Pool programmatically since I want to hide the details from my user.
However using:
ActorRef actor = context().actorOf(new ClusterRouterPool(new RoundRobinPool(5), //
new ClusterRouterPoolSettings(100, 1, true, "")) //
.props(Props.create(MyRoutedActor.class)),
"workerRouter");
does not route the calls to Routees in the cluster (only the local .
How do I configure the routing correctly?
Try to use ClusterRouterPool
Akka doc says [http://doc.akka.io/docs/akka/2.4/scala/cluster-usage.html]:
Pool - router that creates routees as child actors and deploys them on remote nodes. Each router will have its own routee instances. For example, if you start a router on 3 nodes in a 10-node cluster, you will have 30 routees in total if the router is configured to use one instance per node. The routees created by the different routers will not be shared among the routers. One example of a use case for this type of router is a single master that coordinates jobs and delegates the actual work to routees running on other nodes in the cluster.
The example http://doc.akka.io/docs/akka/2.4/java/cluster-usage.html#Router_with_Pool_of_Remote_Deployed_Routees
akka.actor.deployment {
/statsService/singleton/workerRouter {
router = consistent-hashing-pool
cluster {
enabled = on
max-nr-of-instances-per-node = 3
allow-local-routees = on
use-role = compute
}
}
}
The code to do programmatically (also from the akka docs):
int totalInstances = 100;
int maxInstancesPerNode = 3;
boolean allowLocalRoutees = false;
String useRole = "compute";
ActorRef workerRouter = getContext().actorOf(
new ClusterRouterPool(new ConsistentHashingPool(0),
new ClusterRouterPoolSettings(totalInstances, maxInstancesPerNode,
allowLocalRoutees, useRole)).props(Props
.create(StatsWorker.class)), "workerRouter3");
I have an akka cluster in scala, and this is my code:
val workerRouter = context.actorOf(
ClusterRouterGroup(AdaptiveLoadBalancingGroup(MixMetricsSelector), ClusterRouterGroupSettings( //RoundRobinGroup(Nil)
totalInstances = 1000, routeesPaths = List("/user/worker"),
allowLocalRoutees = true, useRole = Some("workerRole"))).props(),
name = "pool")
As stated here, if I define [*]in_*:-2 out_*:-2 as fetchplan, the query should return only properties and none info about the edges.
OrientGraph graph = new OrientGraph(URL, USER, USER);
try {
Iterable resultList = graph.command(new OSQLSynchQuery("select from #11:0")).setFetchPlan("[*]in_*:-2 out_*:-2").execute();
OrientVertex user = (OrientVertex) resultList.iterator().next();
for (String s : user.getRecord().fieldNames()) {
System.out.println(s);
}
Iterable resultList2 = graph.command(new OSQLSynchQuery("select from #11:0")).execute();
OrientVertex user2 = (OrientVertex) resultList2.iterator().next();
for (String s : user2.getRecord().fieldNames()) {
System.out.println(s);
}
} finally {
graph.shutdown();
}
I'm having the same output (that includes info about edges), with and without fetchplan. What am I doing wrong?
With network protocol, Fetch plan is to optimize network transfer, not to exclude connections. In the case above OrientDB client will fetch the connected edges if excluded by fetch plan.
I have an actor who is routing messages to a group of other actors who act as wrappers on top of a volatile service. So far everythign is great, but I'd like to be able to control how many actors exist acting on this service (since they may represent socket connections or other physical properties) so beign able to manage scaling them would be nice.
I see that there is a remove routee method on the router and it does remove the routes, but is there a way to send a poison pill to my child actors first before they are removed? The docs say that a poison pill message should come through when removing the routee this way but I'm not seeing that happen.
I have code like this
final Collection<Routee> routees = JavaConversions.asJavaCollection(router.routees());
for (final Routee routee : routees.stream()
.limit(numberToRemove)
.collect(toList())) {
router = router.removeRoutee(routee);
}
So looks like I was missing the fact that I had to send a poison pill manually in order to stop my router. Here is a full scala demo app
import akka.actor._
import akka.routing._
case class Add()
case class Remove()
class Worker(id: Integer) extends UntypedActor {
println(s"Made worker $id")
#throws[Exception](classOf[Exception]) override
def preStart(): Unit = {
println(s"Starting $id")
}
#throws[Exception](classOf[Exception]) override
def postStop(): Unit = {
println(s"Stopping $id")
}
#throws[Exception](classOf[Exception])
override def onReceive(message: Any): Unit = message match {
case _ => println(s"Message received on actor $id")
}
}
class Master extends Actor {
var count = 0
def makeWorker() = {
val id = count
count = count + 1
context.actorOf(Props(new Worker(id)))
}
var activeWorkers = Seq.fill(2) {
makeWorker()
}
var router = Router(RoundRobinRoutingLogic(), activeWorkers.map(r => {
context watch r
ActorRefRoutee(r)
}).toIndexedSeq)
def receive = {
case Remove =>
println("Removing route")
val head = router.routees.head.asInstanceOf[ActorRefRoutee].ref
head ! PoisonPill
context unwatch head
router = router.removeRoutee(head)
printRoutes()
case Add =>
println("Adding route")
val worker = makeWorker()
context watch worker
router = router.addRoutee(worker)
printRoutes()
case w: AnyRef =>
printRoutes()
router.route(w, sender())
}
def printRoutes(): Unit ={
val size = router.routees.size
println(s"Total routes $size")
}
}
object Main extends App {
var system = ActorSystem.create("foo")
var master = system.actorOf(Props[Master])
master ! "foo"
master ! Remove
master ! "foo"
master ! "bar"
master ! Add
master ! "biz"
}