io.zbus.mq.Broker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zbus Show documentation
Show all versions of zbus Show documentation
a lightweight yet powerful MQ and RPC to build service bus
package io.zbus.mq;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import io.zbus.kit.JsonKit;
import io.zbus.kit.logging.Logger;
import io.zbus.kit.logging.LoggerFactory;
import io.zbus.mq.Protocol.ServerInfo;
import io.zbus.mq.Protocol.TopicInfo;
import io.zbus.mq.Protocol.TrackerInfo;
import io.zbus.mq.server.MqServer;
import io.zbus.transport.Client.ConnectedHandler;
import io.zbus.transport.Client.DisconnectedHandler;
import io.zbus.transport.EventLoop;
import io.zbus.transport.MessageHandler;
import io.zbus.transport.ServerAddress;
import io.zbus.transport.Session;
public class Broker implements Closeable {
private static final Logger log = LoggerFactory.getLogger(Broker.class);
private BrokerRouteTable routeTable = new BrokerRouteTable();
private Map poolTable = new ConcurrentHashMap();
private Map trackerSubscribers = new ConcurrentHashMap();
private List listeners = new ArrayList();
private EventLoop eventLoop;
private int clientPoolSize = 32;
private int readyTimeout = 3000; //3 seconds
private CountDownLatch ready = new CountDownLatch(1);
private boolean waitCheck = true;
public Broker(){
this(new BrokerConfig());
}
public Broker(BrokerConfig config){
this.eventLoop = new EventLoop();
this.clientPoolSize = config.getClientPoolSize();
List trackerList = config.getTrackerList();
for(ServerAddress serverAddress : trackerList){
addTracker(serverAddress);
}
}
public Broker(String trackerList){
this.eventLoop = new EventLoop();
String[] bb = trackerList.split("[,; ]");
for(String tracker : bb){
tracker = tracker.trim();
if(tracker.isEmpty()) continue;
addTracker(tracker);
}
}
public Broker(String trackerList, String token){
this.eventLoop = new EventLoop();
String[] bb = trackerList.split("[,; ]");
for(String tracker : bb){
tracker = tracker.trim();
if(tracker.isEmpty()) continue;
addTracker(tracker, token);
}
}
public Broker(ServerAddress trackerAddress){
this.eventLoop = new EventLoop();
addTracker(trackerAddress);
}
public Broker(MqServer server){
this(server, null);
}
public Broker(MqServer server, String token){
this.eventLoop = new EventLoop();
this.addTracker(server, token);
}
public void addTracker(MqServer server){
addTracker(server, null);
}
public void addTracker(MqServer server, String token){
ServerAddress trackerAddress = new ServerAddress();
trackerAddress.server = server;
trackerAddress.token = token;
addTracker(trackerAddress);
}
public void addTracker(String trackerAddress){
addTracker(trackerAddress, null);
}
public void addTracker(String trackerAddress, String token){
ServerAddress serverAddress = new ServerAddress(trackerAddress);
serverAddress.token = token;
addTracker(serverAddress);
}
public void addTracker(ServerAddress serverAddress){
final ServerAddress trackerAddress = serverAddress.clone();
if(trackerSubscribers.containsKey(trackerAddress)) return;
final MqClient client = new MqClient(trackerAddress, this.eventLoop);
trackerSubscribers.put(trackerAddress, client);
client.onDisconnected(new DisconnectedHandler() {
@Override
public void onDisconnected() throws IOException {
log.warn("Disconnected from tracker(%s)", trackerAddress);
List toRemove = routeTable.removeTracker(trackerAddress);
if(!toRemove.isEmpty()){
for(ServerAddress serverAddress : toRemove){
removeServer(serverAddress);
}
}
client.ensureConnectedAsync();
}
});
client.onConnected(new ConnectedHandler() {
@Override
public void onConnected() throws IOException {
log.info("Connected to tracker(%s)", trackerAddress);
Message req = new Message();
req.setCommand(Protocol.TRACK_SUB);
req.setToken(trackerAddress.getToken());
client.invokeAsync(req, null);
}
});
client.onMessage(new MessageHandler() {
@Override
public void handle(Message msg, Session session) throws IOException {
if(msg.getStatus() != 200){
log.error(msg.getBodyString());
return;
}
if(!Protocol.TRACK_PUB.equals(msg.getCommand())){
log.error("Unknown message: " + msg);
}
TrackerInfo trackerInfo = JsonKit.parseObject(msg.getBodyString(), TrackerInfo.class);
trackerAddress.setAddress(trackerInfo.serverAddress.address); //!!update remote address!!
if(trackerAddress.getServer() != null){ //InProc, change TrackerInfo
String tcpKey = trackerInfo.serverAddress.toString();
trackerInfo.serverAddress = trackerAddress;
if(trackerInfo.serverTable.containsKey(tcpKey)){
ServerInfo serverInfo = trackerInfo.serverTable.remove(tcpKey);
serverInfo.serverAddress = trackerAddress;
trackerInfo.serverTable.put(trackerAddress.toString(), serverInfo);
for(TopicInfo topicInfo : serverInfo.topicTable.values()){
topicInfo.serverAddress = trackerAddress;
}
}
}
List toRemove = routeTable.updateTracker(trackerInfo);
for(ServerInfo serverInfo : routeTable.serverTable().values()){
addServer(serverInfo, trackerAddress);
}
if(!toRemove.isEmpty()){
for(ServerAddress serverAddress : toRemove){
removeServer(serverAddress);
}
}
if(waitCheck){
ready.countDown();
}
}
});
client.ensureConnectedAsync();
}
private void addServer(final ServerAddress serverAddress, ServerAddress trackerAddress) throws IOException {
MqClientPool pool = null;
synchronized (poolTable) {
pool = poolTable.get(serverAddress);
if(pool != null) return;
try {
if(serverAddress.isSslEnabled()){
MqClient client = new MqClient(trackerAddress, this.eventLoop);
String certificate = client.querySslCertificate(serverAddress.getAddress());
client.close();
if(certificate == null){
throw new IllegalStateException("MqServer SSL enabled, but certificate not found");
}
serverAddress.setCertificate(certificate);
}
pool = new MqClientPool(serverAddress, clientPoolSize, this.eventLoop);
} catch (Exception e) {
log.error(e.getMessage(), e);
return;
}
poolTable.put(serverAddress, pool);
}
final MqClientPool createdPool = pool;
eventLoop.getGroup().submit(new Runnable() {
@Override
public void run() {
try {
for(final ServerNotifyListener listener : listeners){
eventLoop.getGroup().submit(new Runnable() {
@Override
public void run() {
listener.onServerJoin(createdPool);
}
});
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
});
}
private void addServer(final ServerInfo serverInfo, ServerAddress trackerAddress) throws IOException {
addServer(serverInfo.serverAddress, trackerAddress);
}
private void removeServer(final ServerAddress serverAddress) {
final MqClientPool pool;
synchronized (poolTable) {
pool = poolTable.remove(serverAddress);
if(pool == null) return;
}
eventLoop.getGroup().schedule(new Runnable() {
@Override
public void run() {
try {
pool.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}, 1000, TimeUnit.MILLISECONDS); //delay 1s to close to wait other service depended on this broker
for(final ServerNotifyListener listener : listeners){
eventLoop.getGroup().submit(new Runnable() {
@Override
public void run() {
listener.onServerLeave(serverAddress);
}
});
}
}
public EventLoop getEventLoop() {
return eventLoop;
}
@Override
public void close() throws IOException {
for(MqClient client : trackerSubscribers.values()){
client.close();
}
trackerSubscribers.clear();
synchronized (poolTable) {
for(MqClientPool pool : poolTable.values()){
pool.close();
}
poolTable.clear();
}
eventLoop.close();
}
public MqClientPool[] selectClient(ServerSelector selector, Message msg) {
checkReady();
ServerAddress[] serverList = selector.select(routeTable, msg);
if(serverList == null){
return poolTable.values().toArray(new MqClientPool[0]);
}
MqClientPool[] pools = new MqClientPool[serverList.length];
int count = 0;
for(int i=0; i
© 2015 - 2025 Weber Informatics LLC | Privacy Policy