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元数据这块的相关信息,有自动装配、订阅的调用、以及元数据处理类的装配过程。

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/1287.html

(0)
上一篇 2021年7月15日
下一篇 2021年7月15日

相关推荐

发表回复

登录后才能评论