|
|
@@ -3,10 +3,13 @@ package org.jetlinks.community.bridge.server.aliyun;
|
|
|
import cn.hutool.core.lang.Assert;
|
|
|
import cn.hutool.core.lang.Pair;
|
|
|
import cn.hutool.core.map.MapUtil;
|
|
|
+import cn.hutool.extra.spring.SpringUtil;
|
|
|
import com.aliyun.iot.as.bridge.core.config.BridgeConfigConsts;
|
|
|
import com.aliyun.iot.as.bridge.core.handler.DownlinkChannelHandler;
|
|
|
import com.aliyun.iot.as.bridge.core.model.PopClientConfiguration;
|
|
|
import com.aliyun.iot.as.bridge.core.model.Session;
|
|
|
+import lombok.Builder;
|
|
|
+import lombok.Data;
|
|
|
import lombok.Getter;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.hswebframework.web.exception.BusinessException;
|
|
|
@@ -15,15 +18,20 @@ import org.jetlinks.community.bridge.core.DefaultBridgeConfigManager;
|
|
|
import org.jetlinks.community.bridge.core.DefaultDeviceConfigManager;
|
|
|
import org.jetlinks.community.bridge.core.DefaultUplinkChannelHandler;
|
|
|
import org.jetlinks.community.bridge.entity.AliIotBridgeEntity;
|
|
|
+import org.jetlinks.community.bridge.enums.BridgeStatus;
|
|
|
import org.jetlinks.community.bridge.message.AliBridgeMessage;
|
|
|
-import org.jetlinks.community.bridge.server.BridgeServer;
|
|
|
import org.jetlinks.community.bridge.server.Channel;
|
|
|
+import org.jetlinks.community.bridge.service.AliBridgeDeviceService;
|
|
|
+import org.jetlinks.community.bridge.service.AliBridgeService;
|
|
|
+import org.jetlinks.core.cluster.ClusterManager;
|
|
|
import org.jetlinks.core.cluster.ClusterUniqueTask;
|
|
|
import org.jetlinks.core.device.DeviceOperator;
|
|
|
import org.jetlinks.core.device.DeviceRegistry;
|
|
|
import org.jetlinks.core.event.EventBus;
|
|
|
+import org.redisson.api.RedissonClient;
|
|
|
import reactor.core.publisher.*;
|
|
|
import javax.validation.constraints.NotNull;
|
|
|
+import java.io.Serializable;
|
|
|
import java.util.Map;
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
@@ -39,50 +47,43 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
* @createTime 2021年11月30日 11:01:00
|
|
|
*/
|
|
|
@Slf4j
|
|
|
-public class AliBridgeServer implements BridgeServer {
|
|
|
- private Map<String, Channel> channelMap=new ConcurrentHashMap<>();
|
|
|
+public class AliBridgeServer extends ClusterUniqueTask<AliBridgeServer> {
|
|
|
+ private transient Map<String, Channel> channelMap=new ConcurrentHashMap<>();
|
|
|
|
|
|
- private final EventBus eventBus;
|
|
|
+ private transient final EventBus eventBus;
|
|
|
|
|
|
/************ 下行数据处理 ***************/
|
|
|
- private final UnicastProcessor<AliBridgeMessage> receive = UnicastProcessor.create();
|
|
|
+ private transient final UnicastProcessor<AliBridgeMessage> receive = UnicastProcessor.create();
|
|
|
|
|
|
- private final FluxSink<AliBridgeMessage> receiveSink = receive.sink(FluxSink.OverflowStrategy.BUFFER);
|
|
|
+ private transient final FluxSink<AliBridgeMessage> receiveSink = receive.sink(FluxSink.OverflowStrategy.BUFFER);
|
|
|
/************ 下行数据处理 ***************/
|
|
|
|
|
|
+ private transient final DeviceRegistry deviceRegistry;
|
|
|
private AtomicBoolean start=new AtomicBoolean(false);
|
|
|
|
|
|
- private DefaultBridgeConfigManager bridgeConfigManager;
|
|
|
+ private transient DefaultBridgeConfigManager bridgeConfigManager;
|
|
|
|
|
|
+ private transient DefaultBridgeBootstrap bootstrap;
|
|
|
|
|
|
- private DefaultBridgeBootstrap bootstrap;
|
|
|
+ private BridgeStatus status;
|
|
|
|
|
|
- private final String clusterId;
|
|
|
+ private transient AliBridgeGateway bridgeGateway;
|
|
|
+
|
|
|
+ private AliIotBridgeEntity params;
|
|
|
|
|
|
@Getter
|
|
|
private final String id;
|
|
|
- private final DeviceRegistry deviceRegistry;
|
|
|
- private AliBridgeServer(EventBus eventBus, DeviceRegistry deviceRegistry, String clusterId, String id) {
|
|
|
+
|
|
|
+
|
|
|
+ AliBridgeServer(EventBus eventBus, DeviceRegistry deviceRegistry, String id) {
|
|
|
+ super("_cluster_bridge_"+id,SpringUtil.getBean(ClusterManager.class),SpringUtil.getBean(RedissonClient.class));
|
|
|
this.deviceRegistry=deviceRegistry;
|
|
|
this.eventBus = eventBus;
|
|
|
- this.clusterId=clusterId;
|
|
|
this.id=id;
|
|
|
+ this.bridgeGateway= SpringUtil.getBean(AliBridgeGateway.class);
|
|
|
}
|
|
|
|
|
|
- public static Mono<AliBridgeServer> create(EventBus eventBus,DeviceRegistry deviceRegistry, AliIotBridgeEntity bridge,String clusterId) {
|
|
|
- AliBridgeServer aliBridgeServer = new AliBridgeServer(eventBus,deviceRegistry,clusterId,bridge.getId());
|
|
|
- return Mono.just(aliBridgeServer)
|
|
|
- .flatMap(server->server.initBridge(bridge))
|
|
|
- .thenReturn(aliBridgeServer);
|
|
|
- }
|
|
|
-
|
|
|
- public Mono<Void> start(){
|
|
|
- return Mono.fromRunnable(()->this.bootstrap.reconnectBridge());
|
|
|
- }
|
|
|
- public Mono<Void> initBridge(AliIotBridgeEntity params){
|
|
|
- if(start.get()){
|
|
|
- return Mono.empty();
|
|
|
- }
|
|
|
+ public Mono<Void> initBridge(AliIotBridgeEntity params,boolean broadcast){
|
|
|
AliIotBridgeEntity.AccessConfig accessConfig = params.getAccessConfig();
|
|
|
String productKey = accessConfig.getProductKey();
|
|
|
Assert.notNull(productKey,"创建网桥 productKey 不能为空, mapping id {%s}",params.getId());
|
|
|
@@ -96,12 +97,29 @@ public class AliBridgeServer implements BridgeServer {
|
|
|
Assert.notNull(http2Endpoint,"创建网桥 http2Endpoint 不能为空, mapping id {%s}",params.getId());
|
|
|
String regionId = accessConfig.getRegionId();
|
|
|
Assert.notNull(regionId,"创建网桥 regionId 不能为空, mapping id {%s}",params.getId());
|
|
|
+ if(broadcast){
|
|
|
+ //发送广播消息
|
|
|
+ getClusterOperationTopic().publish(Mono.just(OperationMessage.builder().param(params).init(true).build())).subscribe();
|
|
|
+ }
|
|
|
+ if(isReplica()){
|
|
|
+ this.params=params;
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+ if(start.get()){
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+ this.status=params.getState();
|
|
|
bridgeConfigManager = DefaultBridgeConfigManager.of(params.getId(),productKey, null, null, http2Endpoint, authEndpoint, MapUtil.of(Pair.of(BridgeConfigConsts.BRIDGE_INSTANCEID_KEY,params.getAccessConfig().getIotInstanceId())), getPopClientProfile(accessKey, accessSecret, regionId));
|
|
|
bootstrap=new DefaultBridgeBootstrap(params.getId(),bridgeConfigManager);
|
|
|
- if(start.get()){
|
|
|
+ //非主节点、非运行状态、已启动
|
|
|
+ if(isReplica()||!BridgeStatus.running.equals(status)||start.get()){
|
|
|
return Mono.empty();
|
|
|
}
|
|
|
if (start.compareAndSet(false, true)) {
|
|
|
+ if(bootstrap.isBridgeConnected()){
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
return Mono.fromRunnable(()->{
|
|
|
bootstrap.bootstrap(new DownlinkChannelHandler() {
|
|
|
@Override
|
|
|
@@ -120,9 +138,9 @@ public class AliBridgeServer implements BridgeServer {
|
|
|
}
|
|
|
params.setDeviceName(bridgeConfigManager.getDeviceName());
|
|
|
return Mono.empty();
|
|
|
-
|
|
|
}
|
|
|
|
|
|
+
|
|
|
private PopClientConfiguration getPopClientProfile(@NotNull String accessKey,@NotNull String accessSecret,@NotNull String endpoint){
|
|
|
PopClientConfiguration popClientConfiguration = new PopClientConfiguration();
|
|
|
popClientConfiguration.setAccessKey(accessKey);
|
|
|
@@ -134,23 +152,27 @@ public class AliBridgeServer implements BridgeServer {
|
|
|
return popClientConfiguration;
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public boolean verify(Map<String, Object> params) {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public String productId() {
|
|
|
- return null;
|
|
|
- }
|
|
|
|
|
|
- @Override
|
|
|
- public Mono<Channel> register(@NotNull String bridgeId,@NotNull String originalIdentity,@NotNull String productKey,@NotNull String deviceName,@NotNull String deviceSecret) {
|
|
|
+ public Mono<Channel> register(@NotNull String bridgeId,@NotNull String originalIdentity,@NotNull String productKey,@NotNull String deviceName,@NotNull String deviceSecret,
|
|
|
+ boolean broadcast) {
|
|
|
//注册设备信息
|
|
|
DefaultDeviceConfigManager.register(bridgeId,originalIdentity,productKey,deviceName,deviceSecret);
|
|
|
DefaultUplinkChannelHandler uplinkChannelHandler = new DefaultUplinkChannelHandler(bridgeConfigManager, DefaultDeviceConfigManager.getInstance());
|
|
|
channelMap
|
|
|
.putIfAbsent(originalIdentity, new DefaultAliBridgeChannel(originalIdentity, productKey, deviceName, uplinkChannelHandler, deviceRegistry, eventBus));
|
|
|
+ if(broadcast){
|
|
|
+ getClusterOperationTopic()
|
|
|
+ .publish(Mono.just(OperationMessage.builder()
|
|
|
+ .bridgeId(bridgeId)
|
|
|
+ .originalIdentity(originalIdentity)
|
|
|
+ .productKey(productKey)
|
|
|
+ .deviceName(deviceName)
|
|
|
+ .deviceSecret(deviceSecret)
|
|
|
+ .register(true))).then(Mono.empty()).subscribe();
|
|
|
+ }
|
|
|
+ if(isReplica()){
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
return Mono.just(channelMap.get(originalIdentity))
|
|
|
.doOnNext(Channel::init)
|
|
|
.flatMap(channel ->
|
|
|
@@ -160,53 +182,157 @@ public class AliBridgeServer implements BridgeServer {
|
|
|
if(online){
|
|
|
channel.online();
|
|
|
}else {
|
|
|
- channel.doOffline();
|
|
|
+ try {
|
|
|
+ channel.doOffline();
|
|
|
+ }catch (Exception e){}
|
|
|
}
|
|
|
})
|
|
|
.thenReturn(channel)
|
|
|
)
|
|
|
.onErrorResume(error->
|
|
|
Mono.error(()->{
|
|
|
- this.unRegister(originalIdentity);
|
|
|
- return new BusinessException("请检查deviceName和deviceSecret是否填写正确");
|
|
|
- }));
|
|
|
+ this.unRegister(originalIdentity,broadcast);
|
|
|
+ return new BusinessException("请检查deviceName和deviceSecret是否填写正确");
|
|
|
+ }));
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public Mono<Void> unRegister(String originalIdentity) {
|
|
|
+
|
|
|
+ public Mono<Void> unRegister(String originalIdentity,boolean broadcast) {
|
|
|
return Mono.fromRunnable(()->{
|
|
|
Channel channel = channelMap.remove(originalIdentity);
|
|
|
- if(channel!=null){
|
|
|
+ if(broadcast){
|
|
|
+ getClusterOperationTopic()
|
|
|
+ .publish(Mono.just(OperationMessage.builder()
|
|
|
+ .originalIdentity(originalIdentity)
|
|
|
+ .unRegister(true))).then(Mono.empty()).subscribe();
|
|
|
+ }
|
|
|
+ if(!isReplica()&&channel!=null){
|
|
|
channel.close();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
- public Mono<Void> stopBridge() {
|
|
|
+
|
|
|
+ public Mono<Void> pauseBridge() {
|
|
|
return Mono.fromRunnable(()->{
|
|
|
- if(bootstrap!=null){
|
|
|
+ if(bootstrap!=null&&bootstrap.isBridgeConnected()){
|
|
|
bootstrap.disconnectBridge();
|
|
|
}
|
|
|
- log.info("网桥[{}]关闭",clusterId);
|
|
|
+ changeStatus(BridgeStatus.stop);
|
|
|
+ log.info("网桥[{}]关闭",id);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
+
|
|
|
+ public Mono<Void> delBridge() {
|
|
|
+ return Mono.fromRunnable(()->{
|
|
|
+ if(bootstrap!=null&&bootstrap.isBridgeConnected()){
|
|
|
+ bootstrap.disconnectBridge();
|
|
|
+ bootstrap=null;
|
|
|
+ }
|
|
|
+ changeStatus(BridgeStatus.del);
|
|
|
+ log.info("网桥[{}]关闭",id);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
public Mono<Void> reconnect() {
|
|
|
return Mono.fromRunnable(()->{
|
|
|
if(bootstrap!=null){
|
|
|
- bootstrap.reconnectBridge();
|
|
|
- channelMap.values().forEach(Channel::online);
|
|
|
+ if(!isReplica()){
|
|
|
+ bootstrap.reconnectBridge();
|
|
|
+ channelMap.values().forEach(Channel::online);
|
|
|
+ }
|
|
|
}
|
|
|
- log.info("网桥[{}]重启成功",clusterId);
|
|
|
+ changeStatus(BridgeStatus.running);
|
|
|
+ log.info("网桥[{}]重启成功",id);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
|
|
|
- @Override
|
|
|
+ private void changeStatus(BridgeStatus status){
|
|
|
+ this.status=status;
|
|
|
+ if(isReplica()){
|
|
|
+ getClusterOperationTopic()
|
|
|
+ .publish(Mono.just(status)).subscribe();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
public Flux<AliBridgeMessage> handleReceive() {
|
|
|
return receive;
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public void beMasterPostProcessor() {
|
|
|
+ if(BridgeStatus.running.equals(status)){
|
|
|
+ bridgeGateway.initBridge(params,false).subscribe();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Mono<?> handleMsg(Object msg) {
|
|
|
+ if(msg instanceof BridgeStatus){
|
|
|
+ BridgeStatus status= (BridgeStatus) msg;
|
|
|
+ return handleStatus(status,false);
|
|
|
+ }
|
|
|
+ if(msg instanceof OperationMessage){
|
|
|
+ OperationMessage message= (OperationMessage) msg;
|
|
|
+ if(message.register){
|
|
|
+ //设备注册
|
|
|
+ return this.register(message.getBridgeId(),message.getOriginalIdentity(),message.getProductKey(),message.getDeviceName(),message.getDeviceSecret(),false);
|
|
|
+ }else if(message.unRegister){
|
|
|
+ return this.unRegister(message.getOriginalIdentity(),false);
|
|
|
+ }
|
|
|
+ else if(message.init){
|
|
|
+ return bridgeGateway.initBridge(params,false);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+ private Mono<Void> handleStatus(BridgeStatus status,boolean broadcast){
|
|
|
+ switch (status){
|
|
|
+ case del:
|
|
|
+ return bridgeGateway.delBridgeServer(id,broadcast);
|
|
|
+ case stop:
|
|
|
+ return bridgeGateway.pauseBridge(id,broadcast);
|
|
|
+ case running:
|
|
|
+ if(this.bootstrap==null){
|
|
|
+ return bridgeGateway.initBridge(params,broadcast);
|
|
|
+ }
|
|
|
+ case fail:
|
|
|
+ break;
|
|
|
+ default:break;
|
|
|
+ }
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public void beforeHandleMsg() {
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Mono<Void> handlePing(ClusterUniqueTask task) {
|
|
|
+ if(task instanceof AliBridgeServer){
|
|
|
+ AliBridgeServer server= (AliBridgeServer) task;
|
|
|
+ changeStatus(server.status);
|
|
|
+ }
|
|
|
+ return Mono.empty();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Builder
|
|
|
+ @Data
|
|
|
+ private static class OperationMessage implements Serializable {
|
|
|
+ private boolean register;
|
|
|
+ private boolean init;
|
|
|
+ private boolean unRegister;
|
|
|
+ private String originalIdentity;
|
|
|
+ private String bridgeId;
|
|
|
+ private String productKey;
|
|
|
+ private String deviceName;
|
|
|
+ private String deviceSecret;
|
|
|
+ private AliIotBridgeEntity param;
|
|
|
+ }
|
|
|
}
|