当前位置:网站首页>Démarrer avec Apache shenyu

Démarrer avec Apache shenyu

2022-06-25 11:05:00 Manuel de programmation de Xiaoxian

Introduction

J'ai récemment appris quelque chose sur la passerelle,J'ai trouvé.shenyuCette asynchrone、Haute performance、Translingue、Réactif API Passerelle. Il a fallu un jour ou deux pour commencer,Notez - le ici..

Pour plus de détails, veuillez consulter le site officiel

Fonctionnement local

Deux modules sont nécessaires avant le démarrage local:

1-shenyu-admin : Gestion de l'arrière - plan de la configuration des plug - ins et autres informations,La classe de démarrage est la suivante:

ShenyuAdminBootstrap

2-shenyu-bootstrap : Pour lancer le projet,La classe de démarrage est la suivante:

ShenyuBootstrapApplication

3-Vous devez configurer avant de commencerdbInformations sur,Ici, j'ai choisimysql,Modifiershenyu-adminEn bas.application.ymlLe contenu est le suivant:,Puis configurerapplication-mysql.ymlLes informations de connexion dans.

spring:
  profiles:
    active: mysql

4-Enfin, initialiserSQLScript:

incubator-shenyu/db/init/mysql/schema.sql

5-Exécuter deux classes de démarrage Adresse d'accès: http://localhost:9095/#/home Nom d'utilisateur Mot de passe admin 123456

Ici, tout le Service de passerelle est lancé localement. , Mais nous n'avons pas encore accès à nos propres services. .

Démarrer avec l'accès au service

Nous pouvons le faire directementshenyu-examples Trouvé le serveur auquel vous souhaitez accéder demo.Par exemple,http,dubbo,motan,springmvc,springcloudAttendez un peu!.

J'ai choisishenyu-examples-httpPour tester,En fait, c'est unspringbootProjets. Parce que nous avons finalement besoin d'un accès par passerelle , La passerelle doit être consciente , Il faut donc d'abord faire quelques configurations (example Déjà configuré ,Possibilité de modifier, J'ai modifié contextPathEtappName)

application.yml:
shenyu:
  register:
    registerType: http #zookeeper #etcd #nacos #consul
    serverLists: http://localhost:9095 #localhost:2181 #http://localhost:2379 #localhost:8848
    props:
      username: admin
      password: 123456
  client:
      http:
        props:
          contextPath: /api/test
          appName: testApp
          port: 8189

Il s'agit principalement de configurer comment les services que nous avons lancés sont enregistrés dans la passerelle. : registerType Les types de représentation comprennent http,zk,nacosAttendez.,La valeur par défaut ici esthttp. client Certaines identités du Service actuel dans la passerelle sont configurées . Puis vous pouvez démarrer l'application .Ensuite, vous pouvez utiliserpostmanEffectuer un test d'appel.Peut être obtenu à partir dedemoDansHttpTestController Sélectionnez une interface pour accéder directement et via la passerelle pour tester .

Accès direct à l'application actuelle http://localhost:8189/test/payment:

Accès par passerelle http://localhost:9195/api/test/test/payment:

L'adresse d'accès vous permet de spécifier contextPath Ce qui fonctionne . Si le préfixe de chemin pour l'accès basé sur la passerelle n'est pas configuré par nous contextPath L'erreur suivante est affichée : "message": "divide:Can not find selector, please check your configuration!"

Analyse du principe d'application de l'accès

Après avoir fait l'introduction la plus élémentaire , Je ne peux m'empêcher d'explorer la raison d'être . C'est ainsi que l'accès http exampleDepom Il a été trouvé dans le fichier qu'il a introduit un starter(springbootDansstarterJe ne vous présenterai pas) Adresse officielle de présentation:shenyu.apache.org/zh/docs/des…

<dependency>
    <groupId>org.apache.shenyu</groupId>
    <artifactId>shenyu-spring-boot-starter-client-springmvc</artifactId>
    <version>${project.version}</version>
</dependency>

Et trouve ça.starterModule,Découverteshenyu D'autres starter,Par exemple,dubboDe,motanDe,Peut permettreRPC Le cadre se connecte à notre passerelle .

On continue à regarder shenyu-spring-boot-starter-client-springmvc.InShenyuSpringMvcClientConfigurationPlus d'un est défini dansbean,Voir principalement

SpringMvcClientBeanPostProcessor
C'est fait.BeanPostProcessorInterface,InbeanInstanciation、Injection dépendante、 L'exécution appelle à la fin de l'initialisation postProcessAfterInitializationMéthodes.Les sources spécifiques sont les suivantes:
@Override
public Object postProcessAfterInitialization(@NonNull final Object bean, @NonNull final String beanName) throws BeansException {
    // Filter out is not controller out
    if (Boolean.TRUE.equals(isFull) || !hasAnnotation(bean.getClass(), Controller.class)) {
        return bean;
    }
    //Obtenir le chemin,Obtenir d'abordShenyuSpringMvcClientNote, Sinon, obtenez RequestMappingOui.
    final ShenyuSpringMvcClient beanShenyuClient = AnnotationUtils.findAnnotation(bean.getClass(), ShenyuSpringMvcClient.class);
    final String superPath = buildApiSuperPath(bean.getClass());
    // Compatible with previous versions
    if (Objects.nonNull(beanShenyuClient) && superPath.contains("*")) {
        publisher.publishEvent(buildMetaDataDTO(beanShenyuClient, pathJoin(contextPath, superPath)));
        return bean;
    }
    
    // Méthode d'obtention obtenir d'abord ShenyuSpringMvcClientNotes,Analysepath
    final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(bean.getClass());
    for (Method method : methods) {
        final RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
        ShenyuSpringMvcClient methodShenyuClient = AnnotationUtils.findAnnotation(method, ShenyuSpringMvcClient.class);
        methodShenyuClient = Objects.isNull(methodShenyuClient) ? beanShenyuClient : methodShenyuClient;
        // the result of ReflectionUtils#getUniqueDeclaredMethods contains method such as hashCode, wait, toSting
        // add Objects.nonNull(requestMapping) to make sure not register wrong method
        //
        if (Objects.nonNull(methodShenyuClient) && Objects.nonNull(requestMapping)) {
            publisher.publishEvent(buildMetaDataDTO(methodShenyuClient, buildApiPath(method, superPath)));
        }
    }
    
    return bean;
}

// En fin de compte, l'annotation résolue est construite comme suit: MetaDataRegisterDTO,Et à traverspublisher.publishEventEnvoyez - le

private MetaDataRegisterDTO buildMetaDataDTO(@NonNull final ShenyuSpringMvcClient shenyuSpringMvcClient, final String path) {
    return MetaDataRegisterDTO.builder()
            .contextPath(contextPath)  //ymlConfiguré
            .appName(appName)          //ymlConfiguré
            .path(path)
            .pathDesc(shenyuSpringMvcClient.desc())
            .rpcType(RpcTypeEnum.HTTP.getName())
            .enabled(shenyuSpringMvcClient.enabled())
            .ruleName(StringUtils.defaultIfBlank(shenyuSpringMvcClient.ruleName(), path))
            .registerMetaData(shenyuSpringMvcClient.registerMetaData())
            .build();
}

ShenyuClientRegisterEventPublisher
Au - dessuspublisher.publishEventÇa veut direShenyuClientRegisterEventPublisher.
Il est basé surDisruptor Un modèle de production et de consommation mis en œuvre par une file d'attente de haute performance .
OffrepublishEvent Comment produire un message 
Et offreQueueConsumer Pour une consommation asynchrone 
Qui finira parRegisterClientConsumerExecutorPour consommer
private final ShenyuClientRegisterEventPublisher publisher = ShenyuClientRegisterEventPublisher.getInstance();


//Méthode de démarrage,SpécifiéShenyuClientMetadataExecutorSubscriberEtShenyuClientURIExecutorSubscriber,InRegisterClientConsumerExecutor Il est utilisé pour la consommation .
public void start(final ShenyuClientRegisterRepository shenyuClientRegisterRepository) {
    RegisterClientExecutorFactory factory = new RegisterClientExecutorFactory();
    factory.addSubscribers(new ShenyuClientMetadataExecutorSubscriber(shenyuClientRegisterRepository));
    factory.addSubscribers(new ShenyuClientURIExecutorSubscriber(shenyuClientRegisterRepository));
    providerManage = new DisruptorProviderManage<>(factory);
    providerManage.startup();
}

//ShenyuClientMetadataExecutorSubscriber Le contenu est de savoir où aller shenyu-adminInscriptionMetadataC'est.

//ShenyuClientRegisterRepository C'est là questarterDéfini dansbean,Voici une introduction,De toute façon, nousexampleCe que j'aiHttpClientRegisterRepository

private final ShenyuClientRegisterRepository shenyuClientRegisterRepository;

/**
 * Instantiates a new shenyu client metadata executor subscriber.
 *
 * @param shenyuClientRegisterRepository the shenyu client register repository
 */
public ShenyuClientMetadataExecutorSubscriber(finalShenyuClientRegisterRepository shenyuClientRegisterRepository) {
    this.shenyuClientRegisterRepository = shenyuClientRegisterRepository;
}

@Override
public DataType getType() {
    return DataType.META_DATA;
}

//Le type de message estDataType.META_DATADe, Le consommateur finira par appeler cette méthode pour traiter le message 
@Override
public void executor(final Collection<MetaDataRegisterDTO> metaDataRegisterDTOList) {
    for (MetaDataRegisterDTO metaDataRegisterDTO : metaDataRegisterDTOList) {
        shenyuClientRegisterRepository.persistInterface(metaDataRegisterDTO);
    }
}

// La mise en œuvre concrète est basée sur httpDemandemetaDataEnregistré àadminMoyenne
@Override
public void doPersistInterface(final MetaDataRegisterDTO metadata) {
    doRegister(metadata, Constants.META_PATH, Constants.META_TYPE);
}

Adresse de l'interface:
String META_PATH = "/shenyu-client/register-metadata";

Nous pouvonsshenyu-adminDansShenyuClientHttpRegistryController Trouver l'adresse correspondante dans .
shenyu-admin La façon de recevoir de nouveaux changements d'information sera expliquée plus loin. .Voici d'abord.
ContextRegisterListener
 Au démarrage publisherProduction moyenneURIRegisterDTOType de message
@Override
public void onApplicationEvent(@NonNull final ContextRefreshedEvent contextRefreshedEvent) {
    if (!registered.compareAndSet(false, true)) {
        return;
    }
    if (Boolean.TRUE.equals(isFull)) {
        publisher.publishEvent(buildMetaDataDTO());
    }
    try {
        final int mergedPort = port <= 0 ? PortUtils.findPort(beanFactory) : port;
        publisher.publishEvent(buildURIRegisterDTO(mergedPort));
    } catch (ShenyuException e) {
        throw new ShenyuException(e.getMessage() + "please config ${shenyu.client.http.props.port} in xml/yml !");
    }
}

private URIRegisterDTO buildURIRegisterDTO(final int port) {
    return URIRegisterDTO.builder()
        .contextPath(this.contextPath)
        .appName(appName)
        .protocol(protocol)
        .host(IpUtils.isCompleteHost(this.host) ? this.host : IpUtils.getHost(this.host))
        .port(port)
        .rpcType(RpcTypeEnum.HTTP.getName())
        .build();
}

ShenyuClientRegisterRepository
 Obtenir des implémentations spécifiques en fonction de la configuration ,Par défauthttp
/**
 * New instance shenyu client register repository.
 *
 * @param shenyuRegisterCenterConfig the shenyu register center config
 * @return the shenyu client register repository
 */
public static ShenyuClientRegisterRepository newInstance(final ShenyuRegisterCenterConfig shenyuRegisterCenterConfig) {
    if (!REPOSITORY_MAP.containsKey(shenyuRegisterCenterConfig.getRegisterType())) {
        //spi Le mécanisme obtient une mise en œuvre concrète ,La nôtre.demo- Oui.HttpClientRegisterRepository
        ShenyuClientRegisterRepository result = ExtensionLoader.getExtensionLoader(ShenyuClientRegisterRepository.class).getJoin(shenyuRegisterCenterConfig.getRegisterType());
        result.init(shenyuRegisterCenterConfig);
        ShenyuClientShutdownHook.set(result, shenyuRegisterCenterConfig.getProps());
        REPOSITORY_MAP.put(shenyuRegisterCenterConfig.getRegisterType(), result);
        return result;
    }
    return REPOSITORY_MAP.get(shenyuRegisterCenterConfig.getRegisterType());
}

Ici, notre application a été informée shenyu-admin.

shenyu-admin Comment recevoir des messages mis à jour

shenyu-admin En tant qu'arrière - plan administratif, les données sont stockées dans dbMoyenne, Et synchroniser les données au service de passerelle .

Là - hauthttp example Nous savons déjà que notre service est basé sur ShenyuClientRegisterRepository Comme ça.shenyu-adminPour s'inscrireMetaDataAttendre l'information.

ShenyuClientRegisterRepository Plusieurs implémentations sont instanciées en fonction de notre configuration .Par exemple:http,nacos... Regardez maintenantadmin Voici comment recevoir les messages d'inscription .

Basé surhttp La méthode d'enregistrement est basée sur ShenyuClientHttpRegistryController Interface interne pour recevoir des messages

@PostMapping("/register-metadata")
@ResponseBody
public String registerMetadata(@RequestBody final MetaDataRegisterDTO metaDataRegisterDTO) {
    publisher.publish(metaDataRegisterDTO);
    return ShenyuResultMessage.SUCCESS;
}

Vous pouvez également regarder la base de nacosMode d'inscription,Si elle est basée surnacosInscription,Etshenyu-admin Qui dépendent shenyu-register-client-server-nacos Module pour écouter les informations d'inscription .

//shenyu admin Initialisation au démarrage bean,Et l'inscriptionShenyuClientRegisterRepositoryRéponse faciale
@Bean(destroyMethod = "close")
public ShenyuClientServerRegisterRepository shenyuClientServerRegisterRepository(final ShenyuRegisterCenterConfig shenyuRegisterCenterConfig,
                                                                           final List<ShenyuClientRegisterService> shenyuClientRegisterService) {
    String registerType = shenyuRegisterCenterConfig.getRegisterType();
    ShenyuClientServerRegisterRepository registerRepository = ExtensionLoader.getExtensionLoader(ShenyuClientServerRegisterRepository.class).getJoin(registerType);
    RegisterClientServerDisruptorPublisher publisher = RegisterClientServerDisruptorPublisher.getInstance();
    Map<String, ShenyuClientRegisterService> registerServiceMap = shenyuClientRegisterService.stream().collect(Collectors.toMap(ShenyuClientRegisterService::rpcType, e -> e));
    publisher.start(registerServiceMap);
    registerRepository.init(publisher, shenyuRegisterCenterConfig);
    return registerRepository;
}
//Basé surnacosClasse d'implémentation pour
NacosClientServerRegisterRepository
//Il est dit ci - dessusbean Il est appelé quand initMéthodes.Enfin, on appellesubscribe Méthode d'écoute 

try {
    this.configService = ConfigFactory.createConfigService(nacosProperties);
    this.namingService = NamingFactory.createNamingService(nacosProperties);
} catch (NacosException e) {
    throw new ShenyuException(e);
}

subscribe();

//subscribeLa méthode est finalement appelée à,Est basé surnacosDeAPIPour écouter

private void subscribeMetadata(final String serviceConfigName) {
    registerMetadata(readData(serviceConfigName));
    LOGGER.info("subscribe metadata: {}", serviceConfigName);
    try {
        configService.addListener(serviceConfigName, defaultGroup, new Listener() {

            @Override
            public Executor getExecutor() {
                return null;
            }

            @Override
            public void receiveConfigInfo(final String config) {
                registerMetadata(config);
            }
        });
    } catch (NacosException e) {
        throw new ShenyuException(e);
    }
}

//Enfin, il sera appelépublisher.publish,Et sur la basehttpInterface pour /register-metadata  La mise en œuvre finale est la même .
private void publishMetadata(final String data) {
    LOGGER.info("publish metadata: {}", data);
    publisher.publish(Lists.newArrayList(GsonUtis.getInstance().fromJson(data, MetaDataRegisterDTO.class)));
}

//Ici.publisher  Et ce qui précède décrit publisherLe principe est le même
Est également basé surDisruptor Un modèle de production et de consommation mis en œuvre par une file d'attente de haute performance .

// Le consommateur finira par invoquer  MetadataExecutorSubscriberDans
shenyuClientRegisterService.register(metaDataRegisterDTO);

//Ici.register C'est
public String register(final MetaDataRegisterDTO dto) {
    //handler plugin selector
    String selectorHandler = selectorHandler(dto);
    String selectorId = selectorService.registerDefault(dto, PluginNameAdapter.rpcTypeAdapter(rpcType()), selectorHandler);
    //handler selector rule
    String ruleHandler = ruleHandler();
    RuleDTO ruleDTO = buildRpcDefaultRuleDTO(selectorId, dto, ruleHandler);
    ruleService.registerDefault(ruleDTO);
    //handler register metadata
    registerMetadata(dto);
    //handler context path
    String contextPath = dto.getContextPath();
    if (StringUtils.isNotEmpty(contextPath)) {
        registerContextPath(dto);
    }
    return ShenyuResultMessage.SUCCESS;
}


Basé surhttpLa classe d'implémentation pour est:ShenyuClientRegisterDivideServiceImpl

protected void registerMetadata(final MetaDataRegisterDTO dto) {
    if (dto.isRegisterMetaData()) {
        MetaDataService metaDataService = getMetaDataService();
        MetaDataDO exist = metaDataService.findByPath(dto.getPath());
        metaDataService.saveOrUpdateMetaData(exist, dto);
    }
}

// Il finira dans l'entrepôt , Et a fait un eventPublisher.publishEventFonctionnement, Cette opération consiste à synchroniser l'information sur la passerelle . Plus de détails .
public void saveOrUpdateMetaData(final MetaDataDO exist, final MetaDataRegisterDTO metaDataDTO) {
    DataEventTypeEnum eventType;
    MetaDataDO metaDataDO = MetaDataTransfer.INSTANCE.mapRegisterDTOToEntity(metaDataDTO);
    if (Objects.isNull(exist)) {
        Timestamp currentTime = new Timestamp(System.currentTimeMillis());
        metaDataDO.setId(UUIDUtils.getInstance().generateShortUuid());
        metaDataDO.setDateCreated(currentTime);
        metaDataDO.setDateUpdated(currentTime);
        metaDataMapper.insert(metaDataDO);
        eventType = DataEventTypeEnum.CREATE;
    } else {
        metaDataDO.setId(exist.getId());
        metaDataMapper.update(metaDataDO);
        eventType = DataEventTypeEnum.UPDATE;
    }
    // publish MetaData's event
    eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.META_DATA, eventType,
            Collections.singletonList(MetaDataTransfer.INSTANCE.mapToData(metaDataDO))));
}

Ici, nous avons une vue d'ensemble de MetaData(URIRegisterC'est le même principe.) Données enregistrées du service à shenyu-adminTout le processus, Vous verrez plus loin comment synchroniser les données sur la passerelle .C'est ce qui est mentionné ci - dessus.:eventPublisher.publishEvent(new DataChangedEvent .....)

shenyu-admin Synchroniser les données sur la passerelle

Au - dessuseventPublisher- Oui.ApplicationEventPublisher,C'est...spring Fonction d'écoute de publication incluse .

eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.META_DATA, eventType,
        Collections.singletonList(MetaDataTransfer.INSTANCE.mapToData(metaDataDO))));

// Trouver un endroit pour écouter DataChangedEventDispatcher, Les messages affichés seront onApplicationEventReçu dans la méthode

public void onApplicationEvent(final DataChangedEvent event) {
    for (DataChangedListener listener : listeners) {
        switch (event.getGroupKey()) {
            case APP_AUTH:
                listener.onAppAuthChanged((List<AppAuthData>) event.getSource(), event.getEventType());
                break;
            case PLUGIN:
                listener.onPluginChanged((List<PluginData>) event.getSource(), event.getEventType());
                break;
            case RULE:
                listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());
                break;
            case SELECTOR:
                listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());
                applicationContext.getBean(LoadServiceDocEntry.class).loadDocOnSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());
                break;
            case META_DATA:
                listener.onMetaDataChanged((List<MetaData>) event.getSource(), event.getEventType());
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
        }
    }
}
//listenerIl y a plusieurs classes d'implémentation, Lequel de ces 


//Voyons d'abord.DataSyncConfigurationConfigurer la classe, Qui est configuré pour synchroniser les données à la passerelle 
shenyu-adminDansapplication.yml Où la configuration a été trouvée :
Par défautwebsocket:
  sync:
      websocket:
         enabled: true
      messageMaxSize: 10240
#      zookeeper:
#        url: localhost:2181
#        sessionTimeout: 5000
#        connectionTimeout: 2000
#      http:
#        enabled: true

EtnacosAttendez....
 
Si ouiwebsocketEtlistenerCe qui correspondWebsocketDataChangedListener 
Si ouihttpEtlistenerCe qui correspondHttpLongPollingDataChangedListener
nacosCe qui correspondNacosDataChangedListener
 D'autres peuvent voir par eux - mêmes .

Si elle est basée surwebsocket,admin Sera établi avec le Service de passerelle websocketConnexion, Puis envoyez un message à la passerelle .

shenyu-adminIci.DataChangedListener Et la passerelle SyncDataServiceRéponse faciale.Par exemple,WebsocketDataChangedListener  Ça correspond à la passerelle WebsocketSyncDataService.

 La fonction de synchronisation des données de la passerelle est centrée sur shenyu-sync-data-centerDans le module, Il fournit également une variété de correspondances de mise en œuvre admin Comment synchroniser les données .
 Est également basé sur la configuration pour voir ce qui est réellement utilisé SyncDataService.En bas.websocketConfiguration de:
@Configuration
@ConditionalOnClass(WebsocketSyncDataService.class)
@ConditionalOnProperty(prefix = "shenyu.sync.websocket", name = "urls")


Service d'appel de passerelle

 À propos de la façon dont la passerelle est appelée au service , Celui - ci est principalement basé sur ShenyuWebHandler Pour traiter la demande .
 Celui - ci n'a pas encore fait l'objet d'une étude plus approfondie , Préparez - vous à rester à l'arrière pour continuer à apprendre .

原网站

版权声明
本文为[Manuel de programmation de Xiaoxian]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/176/202206251045467405.html