com.github.sseserver.remote.NacosServiceDiscoveryService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sse-server Show documentation
Show all versions of sse-server Show documentation
Sse server for Spring Boot
package com.github.sseserver.remote;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingFactory;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.listener.Event;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.listener.NamingEvent;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.github.sseserver.springboot.SseServerProperties;
import com.github.sseserver.util.ReferenceCounted;
import com.github.sseserver.util.SpringUtil;
import com.github.sseserver.util.WebUtil;
import com.sun.net.httpserver.HttpPrincipal;
import org.springframework.beans.factory.DisposableBean;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Timestamp;
import java.util.*;
public class NacosServiceDiscoveryService implements ServiceDiscoveryService, DisposableBean {
public static final String METADATA_NAME_DEVICE_ID = "deviceId";
public static final String METADATA_NAME_ACCOUNT = "account";
public static final String METADATA_NAME_PASSWORD = "password";
public static final String PROJECT_NAME = limit(SpringUtil.filterNonAscii(System.getProperty("user.dir")), 12);
public static final String METADATA_VALUE_DEVICE_ID = SpringUtil.filterNonAscii(
PROJECT_NAME + "-" + WebUtil.getIPAddress(WebUtil.port) + "(" + new Timestamp(System.currentTimeMillis()) + ")");
private final NamingService namingService;
private final String account;
private final String serviceName;
private final String groupName;
private final List clusterName;
private final SseServerProperties.ClusterConfig clusterConfig;
private volatile ReferenceCounted> connectionServiceListRef = new ReferenceCounted<>(Collections.emptyList());
private volatile ReferenceCounted> messageRepositoryListRef = new ReferenceCounted<>(Collections.emptyList());
private List instanceList;
private Instance lastRegisterInstance;
private EventListener onEvent;
public NacosServiceDiscoveryService(String groupName,
String serviceName,
String clusterName,
Properties nacosProperties,
SseServerProperties.ClusterConfig clusterConfig) {
this.groupName = groupName;
this.serviceName = serviceName;
this.clusterName = clusterName == null || clusterName.isEmpty() ?
null : Arrays.asList(clusterName.split(","));
this.account = SpringUtil.filterNonAscii(groupName + "-" + METADATA_VALUE_DEVICE_ID);
this.clusterConfig = clusterConfig;
try {
this.namingService = createNamingService(nacosProperties);
} catch (NacosException e) {
throw new IllegalArgumentException(
"com.github.sseserver.remote.NacosServiceDiscoveryService createNamingService fail : " + e, e);
}
try {
subscribe();
} catch (NacosException e) {
throw new IllegalArgumentException(
"com.github.sseserver.remote.NacosServiceDiscoveryService subscribe fail : " + e, e);
}
}
private static String limit(String string, int len) {
return string.length() > len ? string.substring(string.length() - len) : string;
}
public synchronized void subscribe() throws NacosException {
boolean b = invokeNacosBefore();
try {
if (onEvent != null) {
namingService.unsubscribe(serviceName, groupName, clusterName, onEvent);
}
onEvent = this::onEvent;
namingService.subscribe(serviceName, groupName, clusterName, onEvent);
} finally {
invokeNacosAfter(b);
}
}
public void onEvent(Event event) {
if (!(event instanceof NamingEvent)) {
return;
}
NamingEvent namingEvent = ((NamingEvent) event);
List instanceList = this.instanceList = namingEvent.getInstances();
rebuildConnectionService(instanceList);
rebuildMessageRepository(instanceList);
}
public synchronized ReferenceCounted> rebuildConnectionService(List instanceList) {
ReferenceCounted> old = this.connectionServiceListRef;
this.connectionServiceListRef = new ReferenceCounted<>(newConnectionService(instanceList));
if (old != null) {
old.destroy(list -> {
for (RemoteConnectionService service : list) {
service.close();
}
});
}
return old;
}
public synchronized ReferenceCounted> rebuildMessageRepository(List instanceList) {
ReferenceCounted> old = this.messageRepositoryListRef;
this.messageRepositoryListRef = new ReferenceCounted<>(newMessageRepository(instanceList));
if (old != null) {
old.destroy(list -> {
for (RemoteMessageRepository service : list) {
service.close();
}
});
}
return old;
}
public synchronized NamingService createNamingService(Properties properties) throws NacosException {
boolean b = invokeNacosBefore();
Properties systemProperties = System.getProperties();
String keyNacosGrpcCoreSize = "nacos.remote.client.grpc.pool.core.size";
String valueNacosGrpcCoreSize = systemProperties.getProperty(keyNacosGrpcCoreSize);
String keyNacosGrpcMaxSize = "nacos.remote.client.grpc.pool.max.size";
String valueNacosGrpcMaxSize = systemProperties.getProperty(keyNacosGrpcMaxSize);
try {
if (clusterName != null) {
properties.put("clusterName", String.join(",", clusterName));
}
properties.put("namingPollingThreadCount", "1");
properties.put("namingClientBeatThreadCount", "1");
systemProperties.put(keyNacosGrpcCoreSize, "1");
systemProperties.put(keyNacosGrpcMaxSize, "1");
return NamingFactory.createNamingService(properties);
} finally {
invokeNacosAfter(b);
if (valueNacosGrpcCoreSize == null) {
systemProperties.remove(keyNacosGrpcCoreSize);
} else {
systemProperties.put(keyNacosGrpcCoreSize, valueNacosGrpcCoreSize);
}
if (valueNacosGrpcMaxSize == null) {
systemProperties.remove(keyNacosGrpcMaxSize);
} else {
systemProperties.put(keyNacosGrpcMaxSize, valueNacosGrpcMaxSize);
}
}
}
@Override
public synchronized void registerInstance(String ip, int port) {
Instance lastRegisterInstance = this.lastRegisterInstance;
if (lastRegisterInstance != null) {
boolean b = invokeNacosBefore();
try {
namingService.deregisterInstance(serviceName, groupName, lastRegisterInstance);
this.lastRegisterInstance = null;
} catch (NacosException e) {
throw new IllegalStateException(
"com.github.sseserver.remote.NacosServiceDiscoveryService deregisterInstance fail : " + e, e);
} finally {
invokeNacosAfter(b);
}
}
Map metadata = new LinkedHashMap<>(3);
metadata.put(METADATA_NAME_DEVICE_ID, METADATA_VALUE_DEVICE_ID);
metadata.put(METADATA_NAME_ACCOUNT, account);
metadata.put(METADATA_NAME_PASSWORD, UUID.randomUUID().toString().replace("-", ""));
Instance instance = new Instance();
instance.setIp(ip);
instance.setPort(port);
instance.setWeight(1.0D);
if (clusterName != null) {
instance.setClusterName(String.join(",", clusterName));
}
instance.setMetadata(metadata);
boolean b = invokeNacosBefore();
try {
namingService.registerInstance(serviceName, groupName, instance);
this.lastRegisterInstance = instance;
} catch (NacosException e) {
throw new IllegalStateException(
"com.github.sseserver.remote.NacosServiceDiscoveryService registerInstance fail : " + e, e);
} finally {
invokeNacosAfter(b);
}
}
@Override
public boolean isPrimary() {
return clusterConfig.isPrimary();
}
@Override
public HttpPrincipal login(String authorization) {
if (authorization == null || !authorization.startsWith("Basic ")) {
return null;
}
String token = authorization.substring("Basic ".length());
String[] accountAndPassword = new String(Base64.getDecoder().decode(token)).split(":", 2);
if (accountAndPassword.length != 2) {
return null;
}
String account = accountAndPassword[0];
String password = accountAndPassword[1];
Instance instance = selectInstanceByAccount(account);
if (instance == null) {
return null;
}
String dbPassword = getPassword(instance);
if (Objects.equals(dbPassword, password)) {
return new HttpPrincipal(account, password);
}
return null;
}
protected Instance selectInstanceByAccount(String account) {
for (Instance instance : instanceList) {
String itemAccount = getAccount(instance);
if (Objects.equals(itemAccount, account)) {
return instance;
}
}
return null;
}
public List newMessageRepository(List instanceList) {
List list = new ArrayList<>(instanceList.size());
for (Instance instance : instanceList) {
if (isLocalDevice(instance)) {
continue;
}
String account = getAccount(instance);
String password = getPassword(instance);
try {
URL url = new URL(String.format("http://%s:%d", instance.getIp(), instance.getPort()));
RemoteMessageRepository service = new RemoteMessageRepository(url, account, password, clusterConfig.getMessageRepository(), isPrimary());
list.add(service);
} catch (MalformedURLException e) {
throw new IllegalStateException(
String.format("newMessageRepository => new URL fail! account = '%s', password = '%s', IP = '%s', port = %d ",
account, password, instance.getIp(), instance.getPort()), e);
}
}
return list;
}
public List newConnectionService(List instanceList) {
List list = new ArrayList<>(instanceList.size());
for (Instance instance : instanceList) {
if (isLocalDevice(instance)) {
continue;
}
String account = getAccount(instance);
String password = getPassword(instance);
try {
URL url = new URL(String.format("http://%s:%d", instance.getIp(), instance.getPort()));
RemoteConnectionServiceImpl service = new RemoteConnectionServiceImpl(url, account, password, clusterConfig.getConnectionService());
list.add(service);
} catch (MalformedURLException e) {
throw new IllegalStateException(
String.format("newConnectionService => new URL fail! account = '%s', password = '%s', IP = '%s', port = %d ",
account, password, instance.getIp(), instance.getPort()), e);
}
}
return list;
}
protected String getAccount(Instance instance) {
return instance.getMetadata().get(METADATA_NAME_ACCOUNT);
}
protected String getPassword(Instance instance) {
return instance.getMetadata().get(METADATA_NAME_PASSWORD);
}
protected boolean isLocalDevice(Instance instance) {
String deviceId = instance.getMetadata().get(METADATA_NAME_DEVICE_ID);
return Objects.equals(deviceId, METADATA_VALUE_DEVICE_ID);
}
@Override
public ReferenceCounted> getConnectionServiceListRef() {
return connectionServiceListRef.open();
}
@Override
public ReferenceCounted> getMessageRepositoryListRef() {
return messageRepositoryListRef.open();
}
protected boolean invokeNacosBefore() {
boolean isProjectNameNull = System.getProperty("project.name") == null;
if (isProjectNameNull) {
System.setProperty("project.name", PROJECT_NAME);
}
return isProjectNameNull;
}
protected void invokeNacosAfter(boolean isProjectNameNull) {
if (isProjectNameNull) {
System.getProperties().remove("project.name");
}
}
@Override
public void destroy() throws Exception {
namingService.shutDown();
}
}