Soul网关源码学习02

Soul网关源码学习02

数据同步

soul网关中路由规则、插件管理、负载均衡策略、限流熔断等配置,都是支持动态变更,数据的同步做到秒级同步。配置变更导致数据的同步支持 websocket、http、zookeeper、nacos 等多种方式,实现soul-admin和soul-web的数据同步。

websocket 同步

soul-web 启动时与 soul-admin 建立 websocket 连接,之后soul-admin 会推送一次全量数据,后续如果配置数据发生变更,则将增量数据通过 websocket 主动推送给 soul-web。soul-admin基于 tomcat 的 websocket 实现服务端,soul-web 使用 java-websocket 第三方库实现客户端建立连接。

soul-web 客户端

YML中配置使用 websocket 同步

soul :
    sync:
        websocket :
             urls: ws://localhost:9095/websocket

WebsocketSyncDataConfiguration

  @Bean
    public SyncDataService websocketSyncDataService(@NotNull final ObjectProvider<WebsocketConfig> websocketConfig, 
	final ObjectProvider<PluginDataSubscriber> pluginSubscriber,
 	final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers,
	final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {
    //创建Websocket客户端
   	return new WebsocketSyncDataService(websocketConfig.getIfAvailable(WebsocketConfig::new), pluginSubscriber.getIfAvailable(),metaSubscribers.getIfAvailable(Collections::emptyList),authSubscribers.getIfAvailable(Collections::emptyList));
    }
   
    @Bean
    @ConfigurationProperties(prefix = "soul.sync.websocket")
	//websocket配置
    public WebsocketConfig websocketConfig() {
        return new WebsocketConfig();
    }

​ 1. 使用spring.factories的方式加载,WebsocketSyncDataConfiguration。
​ 2. @ConfigurationProperties 将 websocket 的配置信息封装成实体类。
​ 3. 创建Websocket客户端。

WebsocketSyncDataService

 private List<WebSocketClient> clients = new ArrayList<>();

    private final ScheduledThreadPoolExecutor executor;
    
    public WebsocketSyncDataService(final WebsocketConfig websocketConfig,
                                    final PluginDataSubscriber pluginDataSubscriber,
                                    final List<MetaDataSubscriber> metaDataSubscribers,
                                    final List<AuthDataSubscriber> authDataSubscribers) {
        //多个websocket地址
        String[] urls = StringUtils.split(websocketConfig.getUrls(), ",");
        //创建定时任务的线程
        executor = new ScheduledThreadPoolExecutor(urls.length, SoulThreadFactory.create("websocket-connect", true));
        //每个url创建Websocket客户端
        for (String url : urls) {
            try {
                clients.add(new SoulWebsocketClient(
                    new URI(url), Objects.requireNonNull(pluginDataSubscriber), 
                    	metaDataSubscribers, authDataSubscribers));
            } catch (URISyntaxException e) {
                log.error("websocket url({}) is error", url, e);
            }
        }
        try {
            for (WebSocketClient client : clients) {
               //每个url创建Websocket客户端
                boolean success = client.connectBlocking(3000, TimeUnit.MILLISECONDS);
                if (success) {
                    log.info("websocket connection is successful.....");
                } else {
                    log.error("websocket connection is error.....");
                }
                executor.scheduleAtFixedRate(() -> {
                    try {
                        //状态是关闭或未建立连接
                        if (client.isClosed()) {
                            boolean reconnectSuccess = client.reconnectBlocking();
                            if (reconnectSuccess) {
                                log.info("websocket reconnect is successful.....");
                            } else {
                                log.error("websocket reconnection is error.....");
                            }
                        }
                    } catch (InterruptedException e) {
                        log.error("websocket connect is error :{}", e.getMessage());
                    }
                }, 10, 30, TimeUnit.SECONDS);
            }
        } catch (InterruptedException e) {
            log.info("websocket connection...exception....", e);
        }
    }

    //资源关闭
    @Override
    public void close() {
        //关闭socket连接
        for (WebSocketClient client : clients) {
            if (!client.isClosed()) {
                client.close();
            }
        }
        if (Objects.nonNull(executor)) {
            executor.shutdown();
        }
    }

​ 1. 每个url创建websocket客户端与服务端建立连接,因为是阻塞的所以设置超时时间,防止被卡住。
​ 2. 状态isClosed表示关闭或未建立连接,定时任务的线程进行重试,10秒后执行,间隔周期为30秒。
​ 3. 实现AutoCloseable接口对资源的关闭,关闭socket连接。

SoulWebsocketClient

 
    private volatile boolean alreadySync = Boolean.FALSE;
    private final WebsocketDataHandler websocketDataHandler;
    
    public SoulWebsocketClient(final URI serverUri, final PluginDataSubscriber pluginDataSubscriber,
                               final List<MetaDataSubscriber> metaDataSubscribers, 
                               final List<AuthDataSubscriber> authDataSubscribers) {
        super(serverUri);
        this.websocketDataHandler = new WebsocketDataHandler(pluginDataSubscriber, 
        metaDataSubscribers, authDataSubscribers);
    }
    
    @Override
    public void onOpen(final ServerHandshake serverHandshake) {
        if (!alreadySync) {
        	//发送MYSELF到服务端,获取全量数据
            send(DataEventTypeEnum.MYSELF.name());
            alreadySync = true;
        }
    }
    
    @Override
    public void onMessage(final String result) {
    	//接受消息
        handleResult(result);
    }
    
    @Override
    public void onClose(final int i, final String s, final boolean b) {
        this.close();
    }
    
    @Override
    public void onError(final Exception e) {
        this.close();
    }
    
    @SuppressWarnings("ALL")
    private void handleResult(final String result) {
    	//处理消息
        WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class);
        ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType());
        String eventType = websocketData.getEventType();
        String json = GsonUtils.getInstance().toJson(websocketData.getData());
        websocketDataHandler.executor(groupEnum, json, eventType);
    }

​ 1. 建立连接后发送消息到服务端,服务端会发送全量的配置信息给客户端。
​ 2. 接收服务端发送的消息并进行处理,json数据序列化成对象,从EnumMap中获取处理的Handler。
​ 3. 事件对应的Handler。

ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataHandler(pluginDataSubscriber));
ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AuthDataHandler(authDataSubscribers));
ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataHandler(metaDataSubscribers));
ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataHandler(pluginDataSubscriber));
ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataHandler(pluginDataSubscriber));

AbstractDataHandler

    protected abstract List<T> convert(String json);
    protected abstract void doRefresh(List<T> dataList);
    protected abstract void doUpdate(List<T> dataList);
    protected abstract void doDelete(List<T> dataList);

    @Override
    public void handle(final String json, final String eventType) {
        List<T> dataList = convert(json);
        if (CollectionUtils.isNotEmpty(dataList)) {
            DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType);
            switch (eventTypeEnum) {
                case REFRESH:
                case MYSELF:
                    doRefresh(dataList);
                    break;
                case UPDATE:
                case CREATE:
                    doUpdate(dataList);
                    break;
                case DELETE:
                    doDelete(dataList);
                    break;
                default:
                    break;
            }
        }
    }

定义模板方法,不同事件类型调用各自实现类进行更新。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值