org.jgroups.relay_server.RelayService Maven / Gradle / Ivy
The newest version!
package org.jgroups.relay_server;
import io.grpc.stub.StreamObserver;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Bela Ban
* @since 1.0.0
* todo: Add logging instead of System.err.printf
*/
public class RelayService extends RelayServiceGrpc.RelayServiceImplBase {
// protected final Collection> observers=new ConcurrentLinkedQueue<>();
protected final ConcurrentMap members=new ConcurrentHashMap<>();
protected long view_id=0; // global, for all clusters, but who cares
@Override
public StreamObserver connect(StreamObserver responseObserver) {
return new StreamObserver() {
public void onNext(Request req) {
if(req.hasMessage()) {
handleMessage(req.getMessage());
return;
}
if(req.hasJoinReq()) {
handleJoinRequest(req.getJoinReq(), responseObserver);
return;
}
throw new IllegalStateException(String.format("request is illegal: %s", req));
}
public void onError(Throwable t) {
remove(responseObserver);
}
public void onCompleted() {
remove(responseObserver);
}
};
}
@Override
public void leave(LeaveRequest req, StreamObserver responseObserver) {
final String cluster=req.getClusterName();
boolean removed=false;
Address leaver=req.getLeaver();
if(leaver == null)
return;
SynchronizedMap m=members.get(cluster);
if(m != null) {
Map> map=m.getMap();
Lock lock=m.getLock();
lock.lock();
try {
StreamObserver observer=map.remove(leaver);
if(observer != null) {
removed=true;
observer.onCompleted();
}
if(removed)
postView(map);
}
finally {
lock.unlock();
}
}
responseObserver.onNext(Void.newBuilder().build());
responseObserver.onCompleted();
}
@Override
public void dump(Void request, StreamObserver responseObserver) {
String result=dumpDiagnostics();
responseObserver.onNext(DumpResponse.newBuilder().setDump(result).build());
responseObserver.onCompleted();
}
protected void remove(StreamObserver observer) {
if(observer == null)
return;
for(Map.Entry entry : members.entrySet()) {
String cluster=entry.getKey();
SynchronizedMap m=entry.getValue();
Map> map=m.getMap();
Lock lock=m.getLock();
lock.lock();
try {
map.values().removeIf(val -> Objects.equals(val, observer));
if(map.isEmpty())
members.remove(cluster);
else
postView(map);
}
finally {
lock.unlock();
}
}
}
protected void handleJoinRequest(JoinRequest join_req, StreamObserver responseObserver) {
final String cluster=join_req.getClusterName();
final Address joiner=join_req.getAddress();
SynchronizedMap m=members.computeIfAbsent(cluster, k -> new SynchronizedMap(new LinkedHashMap()));
Map> map=m.getMap();
Lock lock=m.getLock();
lock.lock();
try {
if(map.putIfAbsent(joiner, responseObserver) == null)
postView(map);
}
finally {
lock.unlock();
}
}
protected void handleMessage(Message msg) {
String cluster=msg.getClusterName();
Address dest=msg.hasDestination()? msg.getDestination() : null;
SynchronizedMap mbrs=members.get(cluster);
if(mbrs == null) {
System.err.printf("no members found for cluster %s\n", cluster);
return;
}
if(dest == null)
relayToAll(msg, mbrs);
else
relayTo(msg, mbrs);
}
protected void relayToAll(Message msg, SynchronizedMap m) {
Map> map=m.getMap();
Lock lock=m.getLock();
lock.lock();
try {
if(!map.isEmpty()) {
System.out.printf("-- relaying msg to %d members for cluster %s\n", map.size(), msg.getClusterName());
Response response=Response.newBuilder().setMessage(msg).build();
for(StreamObserver obs: map.values()) {
try {
obs.onNext(response);
}
catch(Throwable t) {
System.out.printf("exception relaying message (removing observer): %s\n", t);
remove(obs);
}
}
}
}
finally {
lock.unlock();
}
}
protected void relayTo(Message msg, SynchronizedMap m) {
Address dest=msg.getDestination();
Map> map=m.getMap();
Lock lock=m.getLock();
lock.lock();
try {
StreamObserver obs=map.get(dest);
if(obs == null) {
System.err.printf("unicast destination %s not found; dropping message\n", dest.getName());
return;
}
System.out.printf("-- relaying msg to member %s for cluster %s\n", dest.getName(), msg.getClusterName());
Response response=Response.newBuilder().setMessage(msg).build();
try {
obs.onNext(response);
}
catch(Throwable t) {
System.out.printf("exception relaying message to %s (removing observer): %s\n", dest.getName(), t);
remove(obs);
}
}
finally {
lock.unlock();
}
}
protected void postView(Map> map) {
View.Builder view_builder=View.newBuilder();
Address coord=null;
for(Address mbr: map.keySet()) {
view_builder.addMember(mbr);
if(coord == null)
coord=mbr;
}
view_builder.setViewId(ViewId.newBuilder().setCreator(coord).setId(getNewViewId()).build());
View new_view=view_builder.build();
Response response=Response.newBuilder().setView(new_view).build();
for(Iterator>> it=map.entrySet().iterator(); it.hasNext();) {
Map.Entry> entry=it.next();
StreamObserver val=entry.getValue();
try {
val.onNext(response);
}
catch(Throwable t) {
it.remove();
}
}
}
protected String dumpDiagnostics() {
StringBuilder sb=new StringBuilder();
sb.append("members:\n");
dumpViews(sb);
return sb.append("\n").toString();
}
protected void dumpViews(final StringBuilder sb) {
for(Map.Entry entry: members.entrySet()) {
String cluster=entry.getKey();
SynchronizedMap m=entry.getValue();
Map> map=m.getMap();
Lock lock=m.getLock();
lock.lock();
try {
sb.append(cluster).append(": ").append(Utils.print(map.keySet())).append("\n");
}
finally {
lock.unlock();
}
}
}
protected synchronized long getNewViewId() {return view_id++;}
protected static class SynchronizedMap {
protected final Map> map;
protected final Lock lock=new ReentrantLock();
public SynchronizedMap(Map> map) {
this.map=map;
}
protected Map> getMap() {return map;}
protected Lock getLock() {return lock;}
}
}