Soul API 网关源码学习《四》

整合sofa插件,接入网关

一、启动服务

作为RPC框架,sofa和dubbo在插件接入上有着一定的相似性。那么我们还是和前面的案例一样,先启动服务,启动服务的顺序是:

  • soul-admin
  • soul-bootstrap
  • soul-examples-sofa

不过在启动服务前,我们先看看配置文件:

1、soul-examples-sofa pom 依赖

<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>soul-spring-boot-starter-client-sofa</artifactId>
    <version>${soul.version}</version>
    <exclusions>
        <exclusion>
            <artifactId>guava</artifactId>
            <groupId>com.google.guava</groupId>
        </exclusion>
    </exclusions>
</dependency>

2、application.yml

server:
  port: 28011
  address: 0.0.0.0
  servlet:
    context-path: /
spring:
  main:
    allow-bean-definition-overriding: true
  application:
    name: sofa
com:
  alipay:
    sofa:
      rpc:
        registry-address: zookeeper://127.0.0.1:2181
        bolt-port: 8888
soul:
  sofa:
    adminUrl: http://localhost:9095
    contextPath: /sofa
    appName: sofa

3、启动类

@SpringBootApplication
@ImportResource({
    
     "classpath*:invoke-server-example.xml"})
public class TestSofaApplication {
    
    
    /**
     * Main Entrance.
     *
     * @param args startup arguments
     */
    public static void main(final String[] args) {
    
    
        SpringApplication.run(TestSofaApplication.class, args);
    }
}

注意:记得启动 zk、在启动admin后开启sofa插件

二、关于sofa 的元数据同步

1、自动装配

这是源于 Spring Boot 的自动装配原理:

图片

这里我们可以看到这个位置是在我们的sofa-example的pom文件中引用的那个模块。然后我们可以看看SofaPluginConfiguration这个类:

@Configuration
@ConditionalOnClass(SofaPlugin.class)
public class SofaPluginConfiguration {
    
    
    /**
     * Sofa plugin soul plugin.
     *
     * @param sofaParamResolveService the sofa param resolve service
     * @return the soul plugin
     */
    @Bean
    public SoulPlugin sofaPlugin(final ObjectProvider<SofaParamResolveService> sofaParamResolveService) {
    
    
        return new SofaPlugin(new SofaProxyService(sofaParamResolveService.getIfAvailable()));
    }
    /**
     * Body param plugin soul plugin.
     *
     * @return the soul plugin
     */
    @Bean
    public SoulPlugin sofaBodyParamPlugin() {
    
    
        return new BodyParamPlugin();
    }
    /**
     * Dubbo response plugin soul plugin.
     *
     * @return the soul plugin
     */
    @Bean
    public SoulPlugin sofaResponsePlugin() {
    
    
        return new SofaResponsePlugin();
    }
    /**
     * Sofa plugin data handler plugin data handler.
     *
     * @return the plugin data handler
     */
    @Bean
    public PluginDataHandler sofaPluginDataHandler() {
    
    
        return new SofaPluginDataHandler();
    }
    /**
     * Sofa meta data subscriber meta data subscriber.
     *
     * @return the meta data subscriber
     */
    @Bean
    public MetaDataSubscriber sofaMetaDataSubscriber() {
    
    
        return new SofaMetaDataSubscriber();
    }
}

从上面代码中,我们可以看到SofaPluginConfiguration会根据SofaPlugin这个条件,来装配我们的Bean,在这里面会装配SofaPlugin、BodyParamPlugin、PluginDataHandler、MetaDataSubscriber。

2、元数据订阅调用

这里面的MetaDataSubscriber是负责订阅admin发布的元数据的更新的。那我们可以看看订阅和取消订阅的调用代码:

public class SofaMetaDataSubscriber implements MetaDataSubscriber {
    
    
    private static final ConcurrentMap<String, MetaData> META_DATA = Maps.newConcurrentMap();
    @Override
    public void onSubscribe(final MetaData metaData) {
    
    
        if (RpcTypeEnum.SOFA.getName().equals(metaData.getRpcType())) {
    
    
            MetaData exist = META_DATA.get(metaData.getPath());
            if (Objects.isNull(exist) || Objects.isNull(ApplicationConfigCache.getInstance().get(exist.getPath()).refer())) {
    
    
                // The first initialization
                ApplicationConfigCache.getInstance().initRef(metaData);
            } else {
    
    
                if (!exist.getServiceName().equals(metaData.getServiceName()) || !exist.getRpcExt().equals(metaData.getRpcExt())) {
    
    
                    // update
                    ApplicationConfigCache.getInstance().build(metaData);
                }
            }
            META_DATA.put(metaData.getPath(), metaData);
        }
    }
    @Override
    public void unSubscribe(final MetaData metaData) {
    
    
        if (RpcTypeEnum.SOFA.getName().equals(metaData.getRpcType())) {
    
    
            ApplicationConfigCache.getInstance().invalidate(metaData.getPath());
            META_DATA.remove(metaData.getPath());
        }
    }
}

感兴趣的童鞋可以自己去debug看看代码的调用链路。不过和前面几个示例是类似的。这里笔者只做简单的代码展示分析。而订阅的调用的位置是在MetaDataHandler类中:

 */
@RequiredArgsConstructor
public class MetaDataHandler extends AbstractDataHandler<MetaData> {
    
    
    private final List<MetaDataSubscriber> metaDataSubscribers;
    @Override
    public List<MetaData> convert(final String json) {
    
    
        return GsonUtils.getInstance().fromList(json, MetaData.class);
    }
    @Override
    protected void doRefresh(final List<MetaData> dataList) {
    
    
        metaDataSubscribers.forEach(MetaDataSubscriber::refresh);
        dataList.forEach(metaData -> metaDataSubscribers.forEach(metaDataSubscriber -> metaDataSubscriber.onSubscribe(metaData)));
    }
    @Override
    protected void doUpdate(final List<MetaData> dataList) {
    
    
        dataList.forEach(metaData -> metaDataSubscribers.forEach(metaDataSubscriber -> metaDataSubscriber.onSubscribe(metaData)));
    }
    @Override
    protected void doDelete(final List<MetaData> dataList) {
    
    
        dataList.forEach(metaData -> metaDataSubscribers.forEach(metaDataSubscriber -> metaDataSubscriber.unSubscribe(metaData)));
    }
}

从上面代码中,可以看到订阅的调用时机是在doRefresh、doUpdate、doDelete这几个方法里的。
但是我们还是要关注MetaDataHandler是如何生成的:

图片

3、处理相关类的装配过程

接着我们来看看WebsocketDataHandler类的生成时机:

图片

接下来就是SoulWebsocketClient类的生成了:

图片

最后看到的是SoulWebsocketClient的装配位置了:

@Configuration
@ConditionalOnClass(WebsocketSyncDataService.class)
@ConditionalOnProperty(prefix = "soul.sync.websocket", name = "urls")
@Slf4j
public class WebsocketSyncDataConfiguration {
    
    
    /**
     * Websocket sync data service.
     *
     * @param websocketConfig   the websocket config
     * @param pluginSubscriber the plugin subscriber
     * @param metaSubscribers   the meta subscribers
     * @param authSubscribers   the auth subscribers
     * @return the sync data service
     */
    @Bean
    public SyncDataService websocketSyncDataService(final ObjectProvider<WebsocketConfig> websocketConfig, final ObjectProvider<PluginDataSubscriber> pluginSubscriber,
                                           final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers, final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {
    
    
        log.info("you use websocket sync soul data.......");
        return new WebsocketSyncDataService(websocketConfig.getIfAvailable(WebsocketConfig::new), pluginSubscriber.getIfAvailable(),
                metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList));
    }
    /**
     * Config websocket config.
     *
     * @return the websocket config
     */
    @Bean
    @ConfigurationProperties(prefix = "soul.sync.websocket")
    public WebsocketConfig websocketConfig() {
    
    
        return new WebsocketConfig();
    }
}

总结

由于时间原因只能做了一个简单的分析,其中的很多细节还没有涉及到,当然也有一些处理的细节和前面的是一样的,譬如bean的前置处理等等。

本篇只是简单的介绍了启动服务的过程及配置文件的注意点,最后就是关于sofa元数据这块的相关信息,有自动装配、订阅的调用、以及元数据处理类的装配过程。

猜你喜欢

转载自blog.csdn.net/zfy163520/article/details/112796979