Replying an Ask in a clustered routee - akka.net

I am creating a project that at this moment has an Actor (User) that calls to another actor (Concert) through a consistent-hash-group router. All works fine but my problem is that from the concert actor I can not answer the Ask message. Somehow the message is lost and nothing happens in the client. I have tried everything with no luck:
Sender.Tell <-- creates a temporal? sender
Passing the User IActorRef by reference in the message and using it.
Here is the full code: https://github.com/pablocastilla/AkkaConcert
The main details are the following:
User actor:
protected IActorRef concertRouter;
public User(IActorRef concertRouter, int eventId)
{
this.concertRouter = concertRouter;
this.eventId = eventId;
JobStarter = Context.System.Scheduler.ScheduleTellRepeatedlyCancelable(TimeSpan.FromMilliseconds(20),
TimeSpan.FromMilliseconds(1000), Self, new AttemptToStartJob(), Self);
Receive<AttemptToStartJob>(start =>
{
var self = Self;
concertRouter.Ask<Routees>(new GetRoutees()).ContinueWith(tr =>
{
if (tr.Result.Members.Count() > 0)
{
var m = new GetAvailableSeats() { User = self, ConcertId = eventId };
self.Tell(m);
// JobStarter.Cancel();
}
}, TaskContinuationOptions.ExecuteSynchronously);
});
Receive<GetAvailableSeats>(rs =>
{
rs.User = Self;
//get free seats
concertRouter.Ask(rs).ContinueWith(t=>
{
Console.WriteLine("response received!!");
}
);
});
Client HOCON configuration:
<akka>
<hocon>
<![CDATA[
akka {
actor {
provider = "Akka.Cluster.ClusterActorRefProvider, Akka.Cluster"
deployment {
/eventpool {
router = consistent-hashing-group
routees.paths = ["/user/HugeEvent"]
virtual-nodes-factor = 8
cluster {
enabled = on
max-nr-of-instances-per-node = 2
allow-local-routees = off
use-role = cluster
}
}
}
}
remote {
log-remote-lifecycle-events = DEBUG
helios.tcp {
transport-class = "Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote"
applied-adapters = []
transport-protocol = tcp
#will be populated with a dynamic host-name at runtime if left uncommented
#public-hostname = "POPULATE STATIC IP HERE"
hostname = "127.0.0.1"
port = 0
}
}
cluster {
#will inject this node as a self-seed node at run-time
seed-nodes = ["akka.tcp://akkaconcert#127.0.0.1:8080"] #manually populate other seed nodes here, i.e. "akka.tcp://lighthouse#127.0.0.1:4053", "akka.tcp://lighthouse#127.0.0.1:4044"
roles = [client]
auto-down-unreachable-after = 60s
}
}
]]>
</hocon>
In the backend side:
The actor is created
private ActorSystem actorSystem;
private IActorRef event1;
public bool Start(HostControl hostControl)
{
actorSystem = ActorSystem.Create("akkaconcert");
SqlServerPersistence.Init(actorSystem);
event1 = actorSystem.ActorOf(
Props.Create(() => new Concert(1,100000)), "HugeEvent");
return true;
}
Concert actor message processing
private void ReadyCommands()
{
Command<GetAvailableSeats>(message => GetFreeSeatsHandler(message));
Command<ReserveSeats>(message => ReserveSeatsHandler(message));
Command<BuySeats>(message => Persist(message, BuySeatsHandler));
}
private bool GetFreeSeatsHandler(GetAvailableSeats message)
{
var freeSeats = seats.Where(s => s.Value.State == Actors.Seat.SeatState.Free).Select(s2 => s2.Value).ToList();
//1. Trying passing the user actor
//message.User.Tell(new GetFreeSeatsResponse() { FreeSeats = freeSeats }, Context.Self);
//2. Trying with the sender
Context.Sender.Tell(new GetAvailableSeatsResponse() { FreeSeats = freeSeats }, Context.Self);
printMessagesPerSecond(messagesReceived++);
printfreeSeats(freeSeats);
return true;
}
HOCON config at backend side:
<akka>
<hocon>
<![CDATA[
akka {
actor {
provider = "Akka.Cluster.ClusterActorRefProvider, Akka.Cluster"
}
remote {
log-remote-lifecycle-events = DEBUG
helios.tcp {
transport-class = "Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote"
applied-adapters = []
transport-protocol = tcp
#will be populated with a dynamic host-name at runtime if left uncommented
#public-hostname = "POPULATE STATIC IP HERE"
hostname = "127.0.0.1"
port = 8080
}
}
cluster {
#will inject this node as a self-seed node at run-time
seed-nodes = ["akka.tcp://akkaconcert#127.0.0.1:8080"] #manually populate other seed nodes here, i.e. "akka.tcp://lighthouse#127.0.0.1:4053", "akka.tcp://lighthouse#127.0.0.1:4044"
roles = [cluster]
auto-down-unreachable-after = 10s
}
}
]]>
</hocon>
Thanks!

The problem comes because of message sizes, the message was too big and it was dropped.
Configuration for receiving bigger messages:
akka {
helios.tcp {
# Maximum frame size: 4 MB
maximum-frame-size = 4000000b
}
}

Related

Using MQTT ManagedClient with ASP NET API, how to?

I'm currently working on a project that has to rely heavily on MQTT - one of the parts that needs to utilize MQTT is a ASP Net API, but I'm having difficulties receiving messages.
Here is my MQTTHandler:
public MQTTHandler()
{
_mqttUrl = Properties.Resources.mqttURL ?? "";
_mqttPort = Properties.Resources.mqttPort ?? "";
_mqttUsername = Properties.Resources.mqttUsername ?? "";
_mqttPassword = Properties.Resources.mqttUsername ?? "";
_mqttFactory = new MqttFactory();
_tls = false;
}
public async Task<IManagedMqttClient> ConnectClientAsync()
{
var clientID = Guid.NewGuid().ToString();
var messageBuilder = new MqttClientOptionsBuilder()
.WithClientId(clientID)
.WithCredentials(_mqttUsername, _mqttPassword)
.WithTcpServer(_mqttUrl, Convert.ToInt32(_mqttPort));
var options = _tls ? messageBuilder.WithTls().Build() : messageBuilder.Build();
var managedOptions = new ManagedMqttClientOptionsBuilder()
.WithAutoReconnectDelay(TimeSpan.FromSeconds(5))
.WithClientOptions(options)
.Build();
_mqttClient = new MqttFactory().CreateManagedMqttClient();
await _mqttClient.StartAsync(managedOptions);
Console.WriteLine("Klient startet");
return _mqttClient;
}
public async Task PublishAsync(string topic, string payload, bool retainFlag = true, int qos = 1)
{
await _mqttClient.EnqueueAsync(new MqttApplicationMessageBuilder()
.WithTopic(topic)
.WithPayload(payload)
.WithQualityOfServiceLevel((MQTTnet.Protocol.MqttQualityOfServiceLevel)qos)
.WithRetainFlag(retainFlag)
.Build());
Console.WriteLine("Besked published");
}
public async Task SubscribeAsync(string topic, int qos = 1)
{
var topicFilters = new List<MQTTnet.Packets.MqttTopicFilter>
{
new MqttTopicFilterBuilder()
.WithTopic(topic)
.WithQualityOfServiceLevel((MQTTnet.Protocol.MqttQualityOfServiceLevel)(qos))
.Build()
};
await _mqttClient.SubscribeAsync(topicFilters);
}
public Status GetSystemStatus(MqttApplicationMessageReceivedEventArgs e)
{
try
{
var json = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
var status = JsonSerializer.Deserialize<Status>(json);
if (status != null)
{
return status;
}
else
{
return null;
}
}
catch (Exception)
{
throw;
}
}
The above has been tested with a console app and works as it should.
The reason I need MQTT in the APi is that a POST method has to act on the value of a topic;
In particular I need to check a systems status before allowing the post;
[HttpPost]
public async Task<ActionResult<Order>> PostOrder(Order order)
{
if (_lastStatus != null)
{
if (_lastStatus.OpStatus)
{
return StatusCode(400, "System is busy!");
}
else
{
var response = await _orderManager.AddOrder(order);
return StatusCode(response.StatusCode, response.Message);
}
}
return StatusCode(400, "Something went wrong");
}
So I will need to set up a subscriber for this controller, and set the value of _lastStatus on received messages:
private readonly MQTTHandler _mqttHandler;
private IManagedMqttClient _mqttClient;
private Status _lastStatus;
public OrdersController(OrderManager orderManager)
{
_orderManager = orderManager;
_mqttHandler = new MQTTHandler();
_mqttClient = _mqttHandler.ConnectClientAsync().Result;
_mqttHandler.SubscribeAsync("JSON/Status");
_mqttClient.ApplicationMessageReceivedAsync += e =>
{
_lastStatus = _mqttHandler.GetSystemStatus(e);
return Task.CompletedTask;
};
}
However, it's behaving a little odd and I'm not experienced enough to know why.
The first time I make a POST request, _lastStatus is null - every following POST request seem to have the last retained message.
I'm guessing that I am struggling due to stuff being asynchronous, but not sure, and every attempt I've attempted to make it synchronous have failed.
Anyone have a clue about what I'm doing wrong?

ASP.NET Core - Redis Sentinel, Getting Error because of ServiceName, HOW TO GET SERVICE NAME?

I have a project with ASP.NET Core integrated with Redis Sentinel.
Caching works very well with Sentinel but it doesn't work while getting all keys with GetServer(), and it wants me to give it a parameter ServiceName, I don't know how to find it??
There is a Master and 4 Slaves
--> appsettings.json
RedisConfiguration:ConnectionString --- > "127.0.0.1:6379,127.0.0.1:6380,127.0.0.1:6381,127.0.0.1:6382,127.0.0.1:6383"
--> RedisCacheManager.cs
Constructor :
public RedisCacheManager()
{
_connectionString = configuration.GetSection("RedisConfiguration:ConnectionString")?.Value;
var connectionStrings = _connectionString.Split(",");
_configurationOptions = new ConfigurationOptions()
{
EndPoints = { aa.ToString() },
AbortOnConnectFail = false,
AsyncTimeout = 10000,
ConnectTimeout = 10000,
KeepAlive = 180,
//ServiceName = ServiceName
};
foreach (var item in connectionStrings)
{
_configurationOptions.EndPoints.Add(item);
}
_client = ConnectionMultiplexer.Connect(_configurationOptions);
}
--> RemoveByPattern method in RedisCacheManager.cs
public void RemoveByPattern()
{
//THIS IS WHERE I USE SENTINEL CONNECT AND CONFIG OPTIONS....
//var aa = ConnectionMultiplexer.SentinelConnect(_configurationOptions);
//THIS IS ALSO WHERE I NEED SERVICE NAME... (FOUND THIS WHILE RESEARCHING)
//var settings = ConfigurationOptions.Parse("localhost,serviceName=mymaster");
// HERE I HAVE TO USE ONLY ONE ENDPOINT....
var server = ConnectionMultiplexer.Connect(settings).GetServer(_client.GetEndPoints().First());
var keys = server.Keys();
var values = keys.Where(x => x.ToString().Contains(pattern)).Select(c => (string)c);
List<string> listKeys = new();
listKeys.AddRange(values);
foreach (var key in listKeys)
{
await _client.GetDatabase().KeyDeleteAsync(key);
}
}

CodenameOne WebRTC CN1LIB Serverless, Manual Signaling

I am trying to breakdown the process of the webrtc signaling using the PeerConnectionStates Demo by copying the signaling strings manually like as in here Youtube:https://www.youtube.com/watch?v=YLPRBYTeoF4&t=1594s .
Github: https://github.com/chrisuehlinger/serverless-webrtc (from 25:00)
With success I would be able to troubleshoot any signaling problems from Pubnub, Firebase or any other signaling solution I may choose.
I have buttons Start, Call, Exchange and Hang.
The 'Exchange' Button shows an Interactive Dialog which has buttons
Copy Offer , Paste Offer, Set Offer, Copy Answer, Paste Answer, Set Answer. And also 'Copy Offer 2 from text file', and 'Copy Answer 2 from text file'.
If you click on the 1st set of the Dialog buttons in that order (excluding the last 2 file selection buttons), you complete the negotiation as in the original demo.
But I want it to be between 2 devices so I put the offer string from device A into a text file and paste into the text field of device B using Copy Offer 2 from text file, then click 'set offer' to generate an answer which I copy into a text file and send to device A using Copy Answer 2 from text file.
I was not able to place a finger on what I was missing. Any help is appreciated.
public class NewClass extends Form implements AutoCloseable {
private RTCVideoElement video1, video2;
private Button startButton = new Button("Start"),
callButton = new Button("Call"),
hangupButton = new Button("Hang up"),
sendOffer = new Button("Exchange"),
sendAnswer = new Button("Send Answer"),
setOffer = new Button("Set Offer"),
setAnswer = new Button("Set Answer"),
copyOffer = new Button("copy Offer"),
pasteOffer = new Button("paste Offer"),
copyAnswer = new Button("copy Answer"),
pasteAnswer = new Button("paste Answer"),
copyOffer2 = new Button("copy Offer 2- from text file"),
setOffer2 = new Button("set Offer 2"),
copyAnswer2 = new Button("copy Answer 2- from text file"),
setAnswer2 = new Button("set Answer 2");
private TextArea toffer1 = new TextArea("", 5, 7, TextArea.ANY),
toffer2 = new TextArea("", 5, 7, TextArea.ANY),
tAnswer1 = new TextArea("", 5, 7, TextArea.ANY),
tAnswer2 = new TextArea("", 5, 7, TextArea.ANY);
{
startButton.setEnabled(true);
callButton.setEnabled(false);
hangupButton.setEnabled(false);
startButton.addActionListener(evt->start());
callButton.addActionListener(evt->call());
hangupButton.addActionListener(evt->hangup());
sendOffer.addActionListener(evt->sendOffer());
// sendAnswer.addActionListener(evt->sendAnswer());
setOffer.addActionListener(evt->setOffer());
setAnswer.addActionListener(evt->setAnswer());
copyOffer.addActionListener(evt->{ Display.getInstance().copyToClipboard(toffer1.getText()); });
pasteOffer.addActionListener(evt->{ toffer2.setText((String)Display.getInstance().getPasteDataFromClipboard()); });
copyAnswer.addActionListener(evt->{ Display.getInstance().copyToClipboard(tAnswer1.getText()); });
pasteAnswer.addActionListener(evt->{ tAnswer2.setText((String)Display.getInstance().getPasteDataFromClipboard()); });
copyOffer2.addActionListener(evt->{
if (FileChooser.isAvailable()) {
FileChooser.showOpenDialog(".xls, .csv, text/plain", e2-> {
String file = (String)e2.getSource();
if (file == null) {
// hi.add("No file was selected");
// hi.revalidate();
} else {
String extension = null;
if (file.lastIndexOf(".") > 0) {
extension = file.substring(file.lastIndexOf(".")+1);
}
if ("txt".equals(extension)) {
FileSystemStorage fs = FileSystemStorage.getInstance();
try {
InputStream fis = fs.openInputStream(file);
// hi.addComponent(new SpanLabel(Util.readToString(fis)));
toffer1.setText(Util.readToString(fis));
} catch (Exception ex) {
Log.e(ex);
}
} else {
// hi.add("Selected file "+file);
}
}
//hi.revalidate();
});
}
});
copyAnswer2.addActionListener(evt->{
if (FileChooser.isAvailable()) {
FileChooser.showOpenDialog("text/plain", e2-> {
String file = (String)e2.getSource();
if (file == null) {
// hi.add("No file was selected");
// hi.revalidate();
} else {
String extension = null;
if (file.lastIndexOf(".") > 0) {
extension = file.substring(file.lastIndexOf(".")+1);
}
if ("txt".equals(extension)) {
FileSystemStorage fs = FileSystemStorage.getInstance();
try {
InputStream fis = fs.openInputStream(file);
// hi.addComponent(new SpanLabel(Util.readToString(fis)));
tAnswer1.setText(Util.readToString(fis));
} catch (Exception ex) {
Log.e(ex);
}
} else {
// hi.add("Selected file "+file);
}
}
//hi.revalidate();
});
}
});
}
InteractionDialog dlg = new InteractionDialog(" - - -");
{dlg.setLayout(new BoxLayout(BoxLayout.Y_AXIS));
dlg.setScrollable(true);
dlg.setDisposeWhenPointerOutOfBounds(true);
dlg.add(" the offer - copy this offer ");dlg.add(copyOffer);dlg.add(copyOffer2);
dlg.addComponent(toffer1); dlg.add("===============================");
dlg.add(" paste offer here ");dlg.add(pasteOffer);
dlg.addComponent(toffer2);
dlg.addComponent(setOffer);
dlg.add("===============================");
dlg.add(" copy this answer ");dlg.add(copyAnswer);dlg.add(copyAnswer2);
dlg.addComponent(tAnswer1);
dlg.add("===============================");
dlg.add("paste answer here");dlg.add(pasteAnswer);
dlg.addComponent(tAnswer2);
dlg.addComponent(setAnswer);
}
private void sendOffer() {
//= call
Dimension pre = dlg.getContentPane().getPreferredSize();
dlg.show(Display.getInstance().getDisplayHeight()/16,0, Display.getInstance().getDisplayWidth()/16, 0);
} /*
private void sendAnswer() {
}*/
private Date startTime;
private MediaStream localStream;
private RTCPeerConnection pc1, pc2;
private SpanLabel pc1StateDiv = new SpanLabel();
private SpanLabel pc2StateDiv = new SpanLabel();
private SpanLabel pc1IceStateDiv = new SpanLabel();
private SpanLabel pc2IceStateDiv = new SpanLabel();
private SpanLabel pc1ConnStateDiv = new SpanLabel();
private SpanLabel pc2ConnStateDiv = new SpanLabel();
private static final RTCOfferOptions offerOptions = new RTCOfferOptions()
.offerToReceiveAudio(true)
.offerToReceiveVideo(true);
private RTC rtc;
public NewClass() {
super("Peer Connection Demo", new BorderLayout());
Container center = new Container(BoxLayout.y());
Container videoCnt = new Container(new BorderLayout());
String intro = "This sample was adapted from the \"PeerConnection: States Demo\" on the WebRTC web site.";
Button viewSource = new Button("View Source");
FontImage.setMaterialIcon(viewSource, FontImage.MATERIAL_LINK);
viewSource.addActionListener(evt->CN.execute("https://github.com/shannah/CN1WebRTC/blob/master/src/com/codename1/webrtc/demos/PeerConnectionStatesDemo.java"));
add(BorderLayout.NORTH, BoxLayout.encloseY(new SpanLabel(intro), viewSource));
//,toffer1,tAnswer1,setAnswer,toffer2,setOffer,tAnswer2
videoCnt.add(BorderLayout.SOUTH, FlowLayout.encloseCenter(startButton, callButton,sendOffer, hangupButton));
videoCnt.setPreferredH(CN.getDisplayHeight()/2);
center.add(videoCnt);
center.add("PC1 state:").
add(pc1StateDiv).
add("PC1 ICE state:").
add(pc1IceStateDiv).
add("PC1 connection state:").
add(pc1ConnStateDiv).
add("PC2 state:").
add(pc2StateDiv).
add("PC2 ICE state:").
add(pc2IceStateDiv).
add("PC2 connection state:").
add(pc2ConnStateDiv).
add(new SpanLabel("View the console to see logging. The MediaStream object localStream, and the RTCPeerConnection objects localPeerConnection and remotePeerConnection are in global scope, so you can inspect them in the console as well."));
center.setScrollableY(true);
add(BorderLayout.CENTER, center);
RTC.createRTC().onSuccess(r->{
rtc = r;
video1 = rtc.createVideo();
video1.setAutoplay(true);
video1.setMuted(true);
video1.applyStyle("position:fixed;width:50%;height:100%;top:0;left:0;bottom:0;");
video2 = rtc.createVideo();
video2.setAutoplay(true);
video2.applyStyle("position:fixed;width:50%;height:100%;top:0;right:0;bottom:0;");
rtc.append(video1);
rtc.append(video2);
video1.onloadedmetadata(evt->{
System.out.println("Local video videoWidth: "+video1.getVideoWidth()+"px, videoHeight: "+video1.getVideoHeight()+"px");
});
video2.onloadedmetadata(evt->{
System.out.println("Remote video size changed to "+video2.getVideoWidth()+"x"+video2.getVideoHeight());
if (startTime != null) {
long elapsedTime = System.currentTimeMillis() - startTime.getTime();
System.out.println("Setup time: "+elapsedTime+"ms");
startTime = null;
}
});
videoCnt.add(BorderLayout.CENTER, rtc.getVideoComponent());
revalidateWithAnimationSafety();
});
}
private void gotStream(MediaStream stream) {
Log.p("Received local stream");
video1.setSrcObject(stream);
localStream = stream;
stream.retain();
callButton.setEnabled(true);
}
private void start() {
Log.p("Requesting local stream");
startButton.setEnabled(false);
rtc.getUserMedia(new MediaStreamConstraints().audio(true).video(true)).onSuccess(stream->{
gotStream(stream);
}).onFail(t->{
Log.e((Throwable)t);
Dialog.show("Error", "getUserMedia() error: "+((Throwable)t).getMessage(), "OK", null);
});
}
private void call() {
callButton.setEnabled(false);
hangupButton.setEnabled(true);
Log.p("Starting call");
startTime = new Date();
MediaStreamTracks videoTracks = localStream.getVideoTracks();
MediaStreamTracks audioTracks = localStream.getAudioTracks();
if (videoTracks.size() > 0) {
Log.p("Using video device "+videoTracks.get(0).getLabel());
}
if (audioTracks.size() > 0) {
Log.p("Using audio device "+audioTracks.get(0).getLabel());
}
RTCConfiguration servers = new RTCConfiguration();
pc1 = rtc.newRTCPeerConnection(servers);
Log.p("Created local peer connection object pc1");
pc1StateDiv.setText(pc1.getSignalingState()+"");
pc1.onsignalingstatechange(evt->stateCallback1());
pc1IceStateDiv.setText(pc1.getIceConnectionState()+"");
pc1.oniceconnectionstatechange(evt->iceStateCallback1())
.onconnectionstatechange(evt->connStateCallback1())
.onicecandidate(e->onIceCandidate(pc1, e));
/* */
pc2 = rtc.newRTCPeerConnection(servers);
Log.p("Created remote peer connection object pc2");
pc2StateDiv.setText(pc2.getSignalingState()+"");
pc2.onsignalingstatechange(evt->stateCallback2());
pc2IceStateDiv.setText(pc2.getIceConnectionState()+"");
pc2.oniceconnectionstatechange(evt->iceStateCallback2())
.onconnectionstatechange(evt->connStateCallback2())
.onicecandidate(evt->{
onIceCandidate(pc2, evt);
});
pc2.ontrack(evt->gotRemoteStream(evt));
for (MediaStreamTrack track : localStream.getTracks()) {
pc1.addTrack(track, localStream);
}
Log.p("Adding local stream to peer connection");
/* */
pc1.createOffer(offerOptions).onSuccess(offer->{
gotDescription1(offer);
})
.onFail(e-> {
onCreateSessionDescriptionError((Throwable)e);
});
}
private void onCreateSessionDescriptionError(Throwable e) {
Log.p("Failed to create session description: "+e.getMessage(), Log.ERROR);
}
private void gotDescription1(RTCSessionDescription description) {
pc1.setLocalDescription(description);
Log.p("Offer from pc1:\n"+description.getSdp());
sendOffer(description.getSdp());
}
private void sendOffer(String jsDesc) {
toffer1.setText(jsDesc);
// getOffer(jsDesc);
}
private void setOffer() {
String desc = toffer2.getText();
if(desc.endsWith("\n")){getOffer(desc);}else{getOffer(desc+"\n");}
}
private void getOffer(String jsDesc) {
RTCSessionDescription description = rtc.createSessionDescription(RTCSessionDescription.RTCSdpType.Offer, jsDesc);
pc2.setRemoteDescription(description).onSuccess(ef->{
})
.onFail(e->onCreateSessionDescriptionError((Throwable)e)); ;
pc2.createAnswer()
.onSuccess(desc->{ sendAnswer(desc.getSdp()); } )
.onFail(e->onCreateSessionDescriptionError((Throwable)e));
}
private void sendAnswer(String jsDesc) {
tAnswer1.setText(jsDesc);
// getAnswer(jsDesc);
}
private void setAnswer() {
String desc = tAnswer2.getText();
//getAnswer(desc+"\n");
//getAnswer(desc);
if(desc.endsWith("\n")){getAnswer(desc);}else{getAnswer(desc+"\n");}
}
private void getAnswer(String jsDesc) {
RTCSessionDescription description = rtc.createSessionDescription(RTCSessionDescription.RTCSdpType.Answer, jsDesc);
gotDescription2(description);
}
private void gotDescription2(RTCSessionDescription description) {
pc2.setLocalDescription(description);
Log.p("Answer from pc2\n"+description.getSdp());
pc1.setRemoteDescription(description);
}
private void hangup() {
Log.p("Ending call");
pc1.close();
pc2.close();
pc1StateDiv.setText(pc1StateDiv.getText() + pc1.getSignalingState());
pc2StateDiv.setText(pc2StateDiv.getText() + pc2.getSignalingState());
pc1.release();
pc1 = null;
pc2.release();
pc2 = null;
hangupButton.setEnabled(false);
callButton.setEnabled(true);
}
private void gotRemoteStream(RTCTrackEvent e) {
if (video2.getSrcObject() != e.getStreams().get(0)) {
video2.setSrcObject(e.getStreams().get(0));
Log.p("Got remote stream");
}
}
private void stateCallback1() {
if (pc1 != null) {
Log.p("pc1 state change callback, state: "+pc1.getSignalingState());
pc1StateDiv.setText(pc1StateDiv.getText() + pc1.getSignalingState());
}
}
private void stateCallback2() {
if (pc2 != null) {
Log.p("pc2 state change callback, state: "+pc2.getSignalingState());
pc2StateDiv.setText(pc2StateDiv.getText() + pc2.getSignalingState());
}
}
private void iceStateCallback1() {
if (pc1 != null) {
Log.p("pc1 ICE connection state change callback, state: "+pc1.getIceConnectionState());
pc1IceStateDiv.setText(pc1IceStateDiv.getText() + pc1.getIceConnectionState());
}
}
private void iceStateCallback2() {
if (pc2 != null) {
Log.p("pc2 ICE connection state change callback, state: "+pc2.getIceConnectionState());
pc2IceStateDiv.setText(pc2IceStateDiv.getText() + pc2.getIceConnectionState());
}
}
private void connStateCallback1() {
if (pc1 != null) {
Log.p("pc1 connection state change callback, state: "+pc1.getConnectionState());
pc1ConnStateDiv.setText(pc1ConnStateDiv.getText() + pc1.getConnectionState());
}
}
private void connStateCallback2() {
if (pc2 != null) {
Log.p("pc2 connection state change callback, state: "+pc2.getConnectionState());
pc2ConnStateDiv.setText(pc2ConnStateDiv.getText() + pc2.getConnectionState());
}
}
private RTCPeerConnection getOtherPc(RTCPeerConnection pc) {
return pc == pc1 ? pc2 : pc1;
}
private String getName(RTCPeerConnection pc) {
return pc == pc1 ? "pc1" : "pc2";
}
private void onIceCandidate(RTCPeerConnection pc, RTCPeerConnectionIceEvent event) {
getOtherPc(pc).addIceCandidate(event.getCandidate())
.onSuccess(res->onAddIceCandidateSuccess(pc))
.onFail(e->{
Log.e((Throwable)e);
onAddIceCandidateError(pc, (Throwable)e);
}).onComplete(e->{
Log.p(getName(pc)+" ICE candidate:\n"+(event.getCandidate() != null ? event.getCandidate().getCandidate() : "(null"));
});
}
private void onAddIceCandidateSuccess(RTCPeerConnection pc) {
Log.p(getName(pc)+" addIceCandidate success");
}
private void onAddIceCandidateError(RTCPeerConnection pc, Throwable error) {
Log.p(getName(pc)+" failed to add ICE Candidate: "+error.getMessage(), Log.ERROR);
}
#Override
public void close() throws Exception {
if (rtc != null) {
rtc.close();
rtc = null;
}
if (pc1 != null) {
pc1.release();
pc1 = null;
}
if (pc2 != null) {
pc2.release();
pc2 = null;
}
}
}
Call is initiated from Phone - as device A to Simulator
On device A - My Phone
PC1 state:
nullHaveLocalOfferStable
PC1 ICE state:
New
PC1 connection state:
PC2 state:
nullHaveRemoteOffer
PC2 ICE state:
New
PC2 connection state:
On Device B - PC Simulator
PC1 state:
nullHaveLocalOfferStable
PC1 ICE state:
NewCheckingConnected
PC1 connection state:
Connecting
PC2 state:
nullHaveRemoteOfferStable
PC2 ICE state:
NewChecking
PC2 connection state:
Connecting
There is an error message:
java.lang.RuntimeException: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Error processing ICE candidate
But the final 3 lines on the console are
[EDT] 0:20:48,103 - pc2 addIceCandidate success
[EDT] 0:20:48,103 - pc2 ICE candidate:
candidate:1503035259 1 udp 7935 154.127.57.220 50066 typ relay raddr 129.205.113.2 rport 6591 generation 0 ufrag lXG+ network-cost 999
My Steps are:
from A
Start Button
Call Button - Creates Offer
Copy Offer from toffer1 (using Exchange Button)
Paste into toffer2
Set Offer Button
on B
6. Start Button
7. Call Button
8. Paste Offer of A into TextArea toffer1 and toffer2 (using Exchange Button)
9. Set Offer Button
10. Copy Answer from tAnswer1
11. Paste Answer into tAnswer2
on A
Paste Answer on tAnswer1 and tAnswer2
Set Answer Button
on B
14. set Answer Button

akka.net cluster : how to send message from seed node to non-seed node

I am new to akka.net. I have 2 .net core2 console apps in a cluster , trying to send message from actor from one console app [ which is seed node ] to remote actor on another console app [which is non-seed node].
After starting/running both the console apps, the cluster is established and both the nodes are Up and seed node knows about the non-seed node and vice-versa, but no message is received by remote actor that is on the non-seed node. I am creating a round-robin router on the seed-node, but not sure what I am missing?
Please guide.
Below is the sample code of the both the apps i.e seed node and non-seed node.
// .net core2 console App with Seed Node
class Program
{
public static ActorSystem ClusterSystem;
private static IActorRef StartActor;
private static void Main(string[] args)
{
var config = ConfigurationFactory.ParseString(#"
akka
{
actor {
provider=cluster
deployment {
/tasker {
router = round-robin-pool # routing strategy
nr-of-instances = 5 # max number of total routees
cluster {
enabled = on
allow-local-routees = off
use-role = tasker
max-nr-of-instances-per-node = 1
}
}
}
}
remote
{
dot-netty.tcp {
port = 8081
hostname = ""localhost""
}
}
cluster {
seed-nodes = [""akka.tcp://ClusterSystem#localhost:8081""]
roles=[""main""]
}
}
ClusterSystem = ActorSystem.Create("ClusterSystem", config);
var taskActor = ClusterSystem.ActorOf(Props.Empty.WithRouter(FromConfig.Instance), "tasker");
StartActor = ClusterSystem.ActorOf(Props.Create(() => new StartActor(taskActor)), "startactor");
StartActor.Tell(new Initiate()); // call local actor
// actor on seed node (local actor)
class StartActor : ReceiveActor, ILogReceive
{
private IActorRef taskActor;
public StartActor(IActorRef router)
{
this.taskActor = router;
Receive<Initiate>(i => Start(i));
}
private void Start(Initiate initiate)
{
taskActor.Tell(new Initiate()); // calling remote actor
}
}
.net core2 Console app with Non seed node
class Program
{
public static ActorSystem ClusterSystem;
public static IActorRef TaskActor;
private static void Main(string[] args)
{
Console.Title = "BackEnd";
var config = ConfigurationFactory.ParseString(#"
akka
{
actor {
provider=cluster
}
remote
{
dot-netty.tcp {
port = 0
hostname = ""localhost""
}
}
cluster {
seed-nodes = [""akka.tcp://ClusterSystem#localhost:8081""]
roles=[""tasker""]
}
}
");
ClusterSystem = ActorSystem.Create("ClusterSystem", config);
TaskActor = ClusterSystem.ActorOf(Props.Create<TaskActor>(), "tasker");
Console.Read();
}
}
// Actor on Non-seed node (Remote Actor)
class TaskActor : ReceiveActor, ILogReceive
{
private readonly IActorRef manager;
public TaskActor()
{
this.Receive<Initiate>(i => this.Init(i));
}
private void Init(Initiate initiate)
{
Console.WriteLine($"Message Received"); //
}
}
I am myself answering to my question. So the first thing is that since the remote actor is created by/in another console application, the deployment configuration needs to be changed with routing strategy to "round robin group"
/tasker {
router = round-robin-group # routing strategy
routees.paths = [""/user/starter""]
nr-of-instances = 5 # max number of total routees
cluster {
enabled = on
allow-local-routees = off
use-role = tasker
}
}
And the the "startActor" from the seed node need to be as below
class StartActor : ReceiveActor, ILogReceive
{
private IActorRef router, self;
public StartActor(IActorRef router)
{
self = Self;
this.router = router;
Receive<Initiate>(i =>
{
var routee = router.Ask<Routees>(new GetRoutees()).ContinueWith(tr =>
{
if (tr.Result.Members.Count() > 0)
{
Start(i);
}
else
{
self.Tell(i);
}
});
});
}
private void Start(Initiate initiate)
{
router.Tell(initiate);
}
}
The above code within the "startActor" looks for the routees which once received then only the message is sent otherwise the message is blasted and not received by the remote actor.

Disassociated exception in Akka.Remoting

Using Akka.net I am trying to implement simple scenario.
I have created 2 servers and 1 client, where Servers receives the messages sent from client and processes it.
Setup works fine sometimes and sometimes it gives following error, I am not able to figure out the cause:
**
No response from remote. Handshake timed out or transport failure detector triggered.
Cause: Unknown
Association with remote system akka.tcp://RemoteFSharp#172.27.**.94:8777 has
failed; address is now gated for 5000 ms. Reason is: [Akka.Remote.EndpointDisass
ociatedException: Disassociated
at Akka.Remote.EndpointWriter.PublishAndThrow(Exception reason, LogLevel leve
l)
at Akka.Remote.EndpointWriter.Unhandled(Object message)
at Akka.Actor.ActorCell.<>c__DisplayClass109_0.<Akka.Actor.IUntypedActorConte
xt.Become>b__0(Object m)
at Akka.Actor.ActorBase.AroundReceive(Receive receive, Object message)
at Akka.Actor.ActorCell.ReceiveMessage(Object message)
at Akka.Actor.ActorCell.AutoReceiveMessage(Envelope envelope)
at Akka.Actor.ActorCell.Invoke(Envelope envelope)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Akka.Actor.ActorCell.HandleFailed(Failed f)
at Akka.Actor.ActorCell.SystemInvoke(Envelope envelope)]
**
Client Config:
akka {
log-dead-letters-during-shutdown = off
actor {
handshake-timeout = 600 s
serializers {
wire = "Akka.Serialization.WireSerializer, Akka.Serialization.Wire"
}
serialization-bindings {
"System.Object" = wire
}
provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"
}
remote {
helios.tcp {
maximum-frame-size = 20000000b
tcp-keepalive = on
transport-class =
"Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote"
transport-protocol = tcp
port = 8760
hostname = 172.27.**.94
}
}
log-remote-lifecycle-events = INFO
}
Server Config :
akka {
log-dead-letters-during-shutdown = off
actor {
handshake-timeout = 600 s
serializers {
wire = "Akka.Serialization.WireSerializer, Akka.Serialization.Wire"
}
serialization-bindings {
"System.Object" = wire
}
provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"
}
remote {
helios.tcp {
maximum-frame-size = 20000000b
tcp-keepalive = on
transport-class =
"Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote"
transport-protocol = tcp
port = 8777
hostname = 172.27.**.94
}
}
log-remote-lifecycle-events = INFO
}
Also I am using Newtonsoft.Json for serialization as follows:
let CreateEmployeeActor (system: ActorSystem) actorName =
(spawn system actorName
(fun mailbox ->
let rec loop (count: int)=
actor {
let! message = mailbox.Receive()
let sender = mailbox.Sender()
let deserializedEmailData = JsonConvert.DeserializeObject<EmployeeActorMsgs> (message)
match deserializedEmailData with
| InItEmployee ->
//Some Logic
}
loop (0)
))