zoukankan      html  css  js  c++  java
  • 微服务:整合 Spring Cloud Eureka

    目录

       微服务:整合 Spring Cloud Eureka - 注册中心 Eureka Server 

       微服务:整合 Spring Cloud Eureka - 服务注册 Eureka Client  

       微服务:整合 Spring Cloud Eureka - 服务发现 DiscoveryClient 

       微服务:整合 Spring Cloud Eureka - 服务消费以及Ribbon简单使用 

       微服务:整合 Spring Cloud Eureka - 高可用集群  

       微服务:整合 Spring Cloud Eureka - .NET Core Mvc Api (C#) 

       微服务:整合 Spring Cloud Eureka - 服务治理机制  

       微服务:整合 Spring Cloud Eureka - 服务事件监听  

       微服务:整合 Spring Cloud Eureka - 高级属性Region、Zone

       微服务:整合 Spring Cloud Eureka - Rest接口文档 

       微服务:整合 Spring Cloud Eureka - Security 安全保护

    一、简介

      当用户地理分布范围很广的时候,比如公司在北京、上海、广州等都有分公司的时候,一般都会有多个机房。那么对于用户而言,当然是希望调用本地分公司的机房中的微服务应用。比如:上海用户A,调用OAuth2服务,用户A当然希望调用上海机房里面的微服务应用。如果上海用户A调用北京机房的OAuth2服务,就增加的延时时间。所以我们希望一个机房内的服务优先调用同一个机房内的服务,当同一个机房的服务不可用的时候,再去调用其它机房的服务,以达到减少延时的作用。

      为此,Eureka提供了Region、Zone参数设置,就是用来解决这个问题。

    二、概念

    eureka提供了region和zone两个概念来进行分区,这两个概念均来自于亚马逊的AWS:

    (1)region:可以简单理解为地理上的分区,比如上海地区,或者广州地区,再或者北京等等,没有具体大小的限制。根据项目具体的情况,可以自行合理划分region。

    (2)zone:可以简单理解为region内的具体机房,比如说region划分为北京,然后北京有两个机房,就可以在此region之下划分出zone1,zone2两个zone。

    三、源码解析

    1、关于Region、Zone的处理类:com.netflix.discovery.endpoint.EndpointUtils.java

      1 package com.netflix.discovery.endpoint;
      2 
      3 import com.netflix.appinfo.InstanceInfo;
      4 import com.netflix.discovery.EurekaClientConfig;
      5 import org.slf4j.Logger;
      6 import org.slf4j.LoggerFactory;
      7 
      8 import java.util.ArrayList;
      9 import java.util.HashMap;
     10 import java.util.LinkedHashMap;
     11 import java.util.List;
     12 import java.util.Map;
     13 import java.util.Set;
     14 import java.util.TreeMap;
     15 import java.util.TreeSet;
     16 
     17 /**
     18  * This class contains some of the utility functions previously found in DiscoveryClient, but should be elsewhere.
     19  * It *does not yet* clean up the moved code.
     20  */
     21 public class EndpointUtils {
     22     private static final Logger logger = LoggerFactory.getLogger(EndpointUtils.class);
     23 
     24     public static final String DEFAULT_REGION = "default";
     25     public static final String DEFAULT_ZONE = "default";
     26 
     27     public enum DiscoveryUrlType {
     28         CNAME, A
     29     }
     30 
     31     public static interface ServiceUrlRandomizer {
     32         void randomize(List<String> urlList);
     33     }
     34 
     35     public static class InstanceInfoBasedUrlRandomizer implements ServiceUrlRandomizer {
     36         private final InstanceInfo instanceInfo;
     37 
     38         public InstanceInfoBasedUrlRandomizer(InstanceInfo instanceInfo) {
     39             this.instanceInfo = instanceInfo;
     40         }
     41 
     42         @Override
     43         public void randomize(List<String> urlList) {
     44             int listSize = 0;
     45             if (urlList != null) {
     46                 listSize = urlList.size();
     47             }
     48             if ((instanceInfo == null) || (listSize == 0)) {
     49                 return;
     50             }
     51             // Find the hashcode of the instance hostname and use it to find an entry
     52             // and then arrange the rest of the entries after this entry.
     53             int instanceHashcode = instanceInfo.getHostName().hashCode();
     54             if (instanceHashcode < 0) {
     55                 instanceHashcode = instanceHashcode * -1;
     56             }
     57             int backupInstance = instanceHashcode % listSize;
     58             for (int i = 0; i < backupInstance; i++) {
     59                 String zone = urlList.remove(0);
     60                 urlList.add(zone);
     61             }
     62         }
     63     }
     64 
     65     /**
     66      * Get the list of all eureka service urls for the eureka client to talk to.
     67      *
     68      * @param clientConfig the clientConfig to use
     69      * @param zone the zone in which the client resides
     70      * @param randomizer a randomizer to randomized returned urls, if loading from dns
     71      *
     72      * @return The list of all eureka service urls for the eureka client to talk to.
     73      */
     74     public static List<String> getDiscoveryServiceUrls(EurekaClientConfig clientConfig, String zone, ServiceUrlRandomizer randomizer) {
     75         boolean shouldUseDns = clientConfig.shouldUseDnsForFetchingServiceUrls();
     76         if (shouldUseDns) {
     77             return getServiceUrlsFromDNS(clientConfig, zone, clientConfig.shouldPreferSameZoneEureka(), randomizer);
     78         }
     79         return getServiceUrlsFromConfig(clientConfig, zone, clientConfig.shouldPreferSameZoneEureka());
     80     }
     81 
     82     /**
     83      * Get the list of all eureka service urls from DNS for the eureka client to
     84      * talk to. The client picks up the service url from its zone and then fails over to
     85      * other zones randomly. If there are multiple servers in the same zone, the client once
     86      * again picks one randomly. This way the traffic will be distributed in the case of failures.
     87      *
     88      * @param clientConfig the clientConfig to use
     89      * @param instanceZone The zone in which the client resides.
     90      * @param preferSameZone true if we have to prefer the same zone as the client, false otherwise.
     91      * @param randomizer a randomizer to randomized returned urls
     92      *
     93      * @return The list of all eureka service urls for the eureka client to talk to.
     94      */
     95     public static List<String> getServiceUrlsFromDNS(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone, ServiceUrlRandomizer randomizer) {
     96         String region = getRegion(clientConfig);
     97         // Get zone-specific DNS names for the given region so that we can get a
     98         // list of available zones
     99         Map<String, List<String>> zoneDnsNamesMap = getZoneBasedDiscoveryUrlsFromRegion(clientConfig, region);
    100         Set<String> availableZones = zoneDnsNamesMap.keySet();
    101         List<String> zones = new ArrayList<String>(availableZones);
    102         if (zones.isEmpty()) {
    103             throw new RuntimeException("No available zones configured for the instanceZone " + instanceZone);
    104         }
    105         int zoneIndex = 0;
    106         boolean zoneFound = false;
    107         for (String zone : zones) {
    108             logger.debug("Checking if the instance zone {} is the same as the zone from DNS {}", instanceZone, zone);
    109             if (preferSameZone) {
    110                 if (instanceZone.equalsIgnoreCase(zone)) {
    111                     zoneFound = true;
    112                 }
    113             } else {
    114                 if (!instanceZone.equalsIgnoreCase(zone)) {
    115                     zoneFound = true;
    116                 }
    117             }
    118             if (zoneFound) {
    119                 logger.debug("The zone index from the list {} that matches the instance zone {} is {}",
    120                         zones, instanceZone, zoneIndex);
    121                 break;
    122             }
    123             zoneIndex++;
    124         }
    125         if (zoneIndex >= zones.size()) {
    126             if (logger.isWarnEnabled()) {
    127                 logger.warn("No match for the zone {} in the list of available zones {}",
    128                         instanceZone, zones.toArray());
    129             }
    130         } else {
    131             // Rearrange the zones with the instance zone first
    132             for (int i = 0; i < zoneIndex; i++) {
    133                 String zone = zones.remove(0);
    134                 zones.add(zone);
    135             }
    136         }
    137 
    138         // Now get the eureka urls for all the zones in the order and return it
    139         List<String> serviceUrls = new ArrayList<String>();
    140         for (String zone : zones) {
    141             for (String zoneCname : zoneDnsNamesMap.get(zone)) {
    142                 List<String> ec2Urls = new ArrayList<String>(getEC2DiscoveryUrlsFromZone(zoneCname, DiscoveryUrlType.CNAME));
    143                 // Rearrange the list to distribute the load in case of multiple servers
    144                 if (ec2Urls.size() > 1) {
    145                     randomizer.randomize(ec2Urls);
    146                 }
    147                 for (String ec2Url : ec2Urls) {
    148                     StringBuilder sb = new StringBuilder()
    149                             .append("http://")
    150                             .append(ec2Url)
    151                             .append(":")
    152                             .append(clientConfig.getEurekaServerPort());
    153                     if (clientConfig.getEurekaServerURLContext() != null) {
    154                         if (!clientConfig.getEurekaServerURLContext().startsWith("/")) {
    155                             sb.append("/");
    156                         }
    157                         sb.append(clientConfig.getEurekaServerURLContext());
    158                         if (!clientConfig.getEurekaServerURLContext().endsWith("/")) {
    159                             sb.append("/");
    160                         }
    161                     } else {
    162                         sb.append("/");
    163                     }
    164                     String serviceUrl = sb.toString();
    165                     logger.debug("The EC2 url is {}", serviceUrl);
    166                     serviceUrls.add(serviceUrl);
    167                 }
    168             }
    169         }
    170         // Rearrange the fail over server list to distribute the load
    171         String primaryServiceUrl = serviceUrls.remove(0);
    172         randomizer.randomize(serviceUrls);
    173         serviceUrls.add(0, primaryServiceUrl);
    174 
    175         if (logger.isDebugEnabled()) {
    176             logger.debug("This client will talk to the following serviceUrls in order : {} ",
    177                     (Object) serviceUrls.toArray());
    178         }
    179         return serviceUrls;
    180     }
    181 
    182     /**
    183      * Get the list of all eureka service urls from properties file for the eureka client to talk to.
    184      *
    185      * @param clientConfig the clientConfig to use
    186      * @param instanceZone The zone in which the client resides
    187      * @param preferSameZone true if we have to prefer the same zone as the client, false otherwise
    188      * @return The list of all eureka service urls for the eureka client to talk to
    189      */
    190     public static List<String> getServiceUrlsFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
    191         List<String> orderedUrls = new ArrayList<String>();
    192         String region = getRegion(clientConfig);
    193         String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
    194         if (availZones == null || availZones.length == 0) {
    195             availZones = new String[1];
    196             availZones[0] = DEFAULT_ZONE;
    197         }
    198         logger.debug("The availability zone for the given region {} are {}", region, availZones);
    199         int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);
    200 
    201         List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[myZoneOffset]);
    202         if (serviceUrls != null) {
    203             orderedUrls.addAll(serviceUrls);
    204         }
    205         int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1);
    206         while (currentOffset != myZoneOffset) {
    207             serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[currentOffset]);
    208             if (serviceUrls != null) {
    209                 orderedUrls.addAll(serviceUrls);
    210             }
    211             if (currentOffset == (availZones.length - 1)) {
    212                 currentOffset = 0;
    213             } else {
    214                 currentOffset++;
    215             }
    216         }
    217 
    218         if (orderedUrls.size() < 1) {
    219             throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!");
    220         }
    221         return orderedUrls;
    222     }
    223 
    224     /**
    225      * Get the list of all eureka service urls from properties file for the eureka client to talk to.
    226      *
    227      * @param clientConfig the clientConfig to use
    228      * @param instanceZone The zone in which the client resides
    229      * @param preferSameZone true if we have to prefer the same zone as the client, false otherwise
    230      * @return an (ordered) map of zone -> list of urls mappings, with the preferred zone first in iteration order
    231      */
    232     public static Map<String, List<String>> getServiceUrlsMapFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
    233         Map<String, List<String>> orderedUrls = new LinkedHashMap<>();
    234         String region = getRegion(clientConfig);
    235         String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
    236         if (availZones == null || availZones.length == 0) {
    237             availZones = new String[1];
    238             availZones[0] = DEFAULT_ZONE;
    239         }
    240         logger.debug("The availability zone for the given region {} are {}", region, availZones);
    241         int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);
    242 
    243         String zone = availZones[myZoneOffset];
    244         List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
    245         if (serviceUrls != null) {
    246             orderedUrls.put(zone, serviceUrls);
    247         }
    248         int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1);
    249         while (currentOffset != myZoneOffset) {
    250             zone = availZones[currentOffset];
    251             serviceUrls = clientConfig.getEurekaServerServiceUrls(zone);
    252             if (serviceUrls != null) {
    253                 orderedUrls.put(zone, serviceUrls);
    254             }
    255             if (currentOffset == (availZones.length - 1)) {
    256                 currentOffset = 0;
    257             } else {
    258                 currentOffset++;
    259             }
    260         }
    261 
    262         if (orderedUrls.size() < 1) {
    263             throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!");
    264         }
    265         return orderedUrls;
    266     }
    267 
    268     /**
    269      * Get the list of EC2 URLs given the zone name.
    270      *
    271      * @param dnsName The dns name of the zone-specific CNAME
    272      * @param type CNAME or EIP that needs to be retrieved
    273      * @return The list of EC2 URLs associated with the dns name
    274      */
    275     public static Set<String> getEC2DiscoveryUrlsFromZone(String dnsName, DiscoveryUrlType type) {
    276         Set<String> eipsForZone = null;
    277         try {
    278             dnsName = "txt." + dnsName;
    279             logger.debug("The zone url to be looked up is {} :", dnsName);
    280             Set<String> ec2UrlsForZone = DnsResolver.getCNamesFromTxtRecord(dnsName);
    281             for (String ec2Url : ec2UrlsForZone) {
    282                 logger.debug("The eureka url for the dns name {} is {}", dnsName, ec2Url);
    283                 ec2UrlsForZone.add(ec2Url);
    284             }
    285             if (DiscoveryUrlType.CNAME.equals(type)) {
    286                 return ec2UrlsForZone;
    287             }
    288             eipsForZone = new TreeSet<String>();
    289             for (String cname : ec2UrlsForZone) {
    290                 String[] tokens = cname.split("\.");
    291                 String ec2HostName = tokens[0];
    292                 String[] ips = ec2HostName.split("-");
    293                 StringBuilder eipBuffer = new StringBuilder();
    294                 for (int ipCtr = 1; ipCtr < 5; ipCtr++) {
    295                     eipBuffer.append(ips[ipCtr]);
    296                     if (ipCtr < 4) {
    297                         eipBuffer.append(".");
    298                     }
    299                 }
    300                 eipsForZone.add(eipBuffer.toString());
    301             }
    302             logger.debug("The EIPS for {} is {} :", dnsName, eipsForZone);
    303         } catch (Throwable e) {
    304             throw new RuntimeException("Cannot get cnames bound to the region:" + dnsName, e);
    305         }
    306         return eipsForZone;
    307     }
    308 
    309     /**
    310      * Get the zone based CNAMES that are bound to a region.
    311      *
    312      * @param region
    313      *            - The region for which the zone names need to be retrieved
    314      * @return - The list of CNAMES from which the zone-related information can
    315      *         be retrieved
    316      */
    317     public static Map<String, List<String>> getZoneBasedDiscoveryUrlsFromRegion(EurekaClientConfig clientConfig, String region) {
    318         String discoveryDnsName = null;
    319         try {
    320             discoveryDnsName = "txt." + region + "." + clientConfig.getEurekaServerDNSName();
    321 
    322             logger.debug("The region url to be looked up is {} :", discoveryDnsName);
    323             Set<String> zoneCnamesForRegion = new TreeSet<String>(DnsResolver.getCNamesFromTxtRecord(discoveryDnsName));
    324             Map<String, List<String>> zoneCnameMapForRegion = new TreeMap<String, List<String>>();
    325             for (String zoneCname : zoneCnamesForRegion) {
    326                 String zone = null;
    327                 if (isEC2Url(zoneCname)) {
    328                     throw new RuntimeException(
    329                             "Cannot find the right DNS entry for "
    330                                     + discoveryDnsName
    331                                     + ". "
    332                                     + "Expected mapping of the format <aws_zone>.<domain_name>");
    333                 } else {
    334                     String[] cnameTokens = zoneCname.split("\.");
    335                     zone = cnameTokens[0];
    336                     logger.debug("The zoneName mapped to region {} is {}", region, zone);
    337                 }
    338                 List<String> zoneCnamesSet = zoneCnameMapForRegion.get(zone);
    339                 if (zoneCnamesSet == null) {
    340                     zoneCnamesSet = new ArrayList<String>();
    341                     zoneCnameMapForRegion.put(zone, zoneCnamesSet);
    342                 }
    343                 zoneCnamesSet.add(zoneCname);
    344             }
    345             return zoneCnameMapForRegion;
    346         } catch (Throwable e) {
    347             throw new RuntimeException("Cannot get cnames bound to the region:" + discoveryDnsName, e);
    348         }
    349     }
    350 
    351     /**
    352      * Get the region that this particular instance is in.
    353      *
    354      * @return - The region in which the particular instance belongs to.
    355      */
    356     public static String getRegion(EurekaClientConfig clientConfig) {
    357         String region = clientConfig.getRegion();
    358         if (region == null) {
    359             region = DEFAULT_REGION;
    360         }
    361         region = region.trim().toLowerCase();
    362         return region;
    363     }
    364 
    365     // FIXME this is no valid for vpc
    366     private static boolean isEC2Url(String zoneCname) {
    367         return zoneCname.startsWith("ec2");
    368     }
    369 
    370     /**
    371      * Gets the zone to pick up for this instance.
    372      */
    373     private static int getZoneOffset(String myZone, boolean preferSameZone, String[] availZones) {
    374         for (int i = 0; i < availZones.length; i++) {
    375             if (myZone != null && (availZones[i].equalsIgnoreCase(myZone.trim()) == preferSameZone)) {
    376                 return i;
    377             }
    378         }
    379         logger.warn("DISCOVERY: Could not pick a zone based on preferred zone settings. My zone - {}," +
    380                 " preferSameZone - {}. Defaulting to {}", myZone, preferSameZone, availZones[0]);
    381 
    382         return 0;
    383     }
    384 }
    View Code

    2、重点解读:getServiceUrlsFromConfig

        /**
         * Get the list of all eureka service urls from properties file for the eureka client to talk to.
         *
         * @param clientConfig the clientConfig to use
         * @param instanceZone The zone in which the client resides
         * @param preferSameZone true if we have to prefer the same zone as the client, false otherwise
         * @return The list of all eureka service urls for the eureka client to talk to
         */
        public static List<String> getServiceUrlsFromConfig(EurekaClientConfig clientConfig, String instanceZone, boolean preferSameZone) {
            List<String> orderedUrls = new ArrayList<String>();
            String region = getRegion(clientConfig);
            String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion());
            if (availZones == null || availZones.length == 0) {
                availZones = new String[1];
                availZones[0] = DEFAULT_ZONE;
            }
            logger.debug("The availability zone for the given region {} are {}", region, availZones);
            int myZoneOffset = getZoneOffset(instanceZone, preferSameZone, availZones);
    
            List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[myZoneOffset]);
            if (serviceUrls != null) {
                orderedUrls.addAll(serviceUrls);
            }
            int currentOffset = myZoneOffset == (availZones.length - 1) ? 0 : (myZoneOffset + 1);
            while (currentOffset != myZoneOffset) {
                serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[currentOffset]);
                if (serviceUrls != null) {
                    orderedUrls.addAll(serviceUrls);
                }
                if (currentOffset == (availZones.length - 1)) {
                    currentOffset = 0;
                } else {
                    currentOffset++;
                }
            }
    
            if (orderedUrls.size() < 1) {
                throw new IllegalArgumentException("DiscoveryClient: invalid serviceUrl specified!");
            }
            return orderedUrls;
        }

    1、通过String region = getRegion(clientConfig),我们可以知道一个微服务应用只能设置一个Region。

    2、通过String[] availZones = clientConfig.getAvailabilityZones(clientConfig.getRegion()),我们可以知道一个Region下可以配置多个zone。

    3、通过 List<String> serviceUrls = clientConfig.getEurekaServerServiceUrls(availZones[myZoneOffset]);,我们可以知道在一个zone下可以配置多个serviceUrl。

    4、当我们设置了Region=shanghai,系统会优先加载Region=shanghai下的Zones。

    5、如果在Region=shanghai下没有可用的zone,系统会默认加载 DEFAULT_ZONE。

    四、Ribbon调用

      当我们在微服务应用中使用Ribbon来实现服务调用时,对于Zone的设置可以在负载均衡是实现区域亲和特性,也就是说,Ribbon的默认策略会优先访问同一个客户端处于一个Zone中的服务端实例。只有当同一个Zone中没有可用的服务端实例的时候才会访问其他Zone中的实例。所以通过Zone属性的定义,配合实际部署的物理结构,我们可以有效的设计出针对区域性的故障的容错集群。

    五、代码配置

     注册中心-1 : application.yml

    server:
      port: 8201
    
    spring:
      application:
        name: demo-service-consumer
    
    eureka:
      instance:
        lease-renewal-interval-in-seconds: 3
        lease-expiration-duration-in-seconds: 9
        hostname: peer1
        metadata-map:
          zone: zone-1
      client:
        register-with-eureka: true
        fetch-registry: true
        instance-info-replication-interval-seconds: 9
        registry-fetch-interval-seconds: 3
        serviceUrl:
          defaultZone: http://peer1:8001/register/eureka/

     注册中心-2 : application.yml

    server:
      port: 9001
      servlet:
        context-path: /register
    
    spring:
      application:
        name: demo-register
    
    eureka:
      instance:
        hostname: peer2
      client:
        register-with-eureka: true
        fetch-registry: true
        instance-info-replication-interval-seconds: 30
        serviceUrl:
          defaultZone: http://peer1:8001/register/eureka/
    demo-service-provider-1 : application.yml
    server:
      port: 8102
    
    spring:
      application:
        name: demo-service-provider
    
    eureka:
      instance:
        lease-renewal-interval-in-seconds: 3
        lease-expiration-duration-in-seconds: 9
        hostname: peer1
        metadata-map:
          zone: zone-1
      client:
        register-with-eureka: true
        fetch-registry: true
        instance-info-replication-interval-seconds: 9
        registry-fetch-interval-seconds: 3
        serviceUrl:
          defaultZone: http://peer1:8001/register/eureka/
    demo-service-provider-2
     : application.yml
    server:
      port: 9102
    
    spring:
      application:
        name: demo-service-provider
    
    
    eureka:
      instance:
        lease-renewal-interval-in-seconds: 3
        lease-expiration-duration-in-seconds: 9
        hostname: peer2
        metadata-map:
          zone: zone-2
      client:
        register-with-eureka: true
        fetch-registry: true
        instance-info-replication-interval-seconds: 9
        registry-fetch-interval-seconds: 3
        serviceUrl:
          defaultZone: http://peer2:9001/register/eureka/
    demo-service-consumer-1 : application.yml
    server:
      port: 8201
    
    spring:
      application:
        name: demo-service-consumer
    
    eureka:
      instance:
        lease-renewal-interval-in-seconds: 3
        lease-expiration-duration-in-seconds: 9
        hostname: peer1
        metadata-map:
          zone: zone-1
      client:
        register-with-eureka: true
        fetch-registry: true
        instance-info-replication-interval-seconds: 9
        registry-fetch-interval-seconds: 3
        serviceUrl:
          defaultZone: http://peer1:8001/register/eureka/

    六、运行测试

    启动: 注册中心-1, 注册中心-2,demo-service-provider-1,demo-service-provider-2,demo-service-consumer-1 

    打开注册中心:http://localhost:8001/register/http://localhost:9001/register/

     

    打开服务消费者:http://localhost:8201/hello/java

    停掉demo-service-provider-1微服务的实例,再次打开服务消费者:http://localhost:8201/hello/java 会有不一样的结果

     测试结果完美!

  • 相关阅读:
    Flutter 布局(九)- Flow、Table、Wrap详解
    Flutter 布局(三)- FittedBox、AspectRatio、ConstrainedBox详解
    广州小程序开发攻略
    Angular2 富文本编辑器 ng2-ckeditor 的使用
    onlyoffice5.4.2离线包下载—解决中文字体问题
    beego+vue父子组件通信(父子页面传值、父子组件传值、父子路由传值)
    beego+vue.js分离开发,结合发布,简单部署
    flow-vue.js移动端效果
    engineecms——工程师知识管理系统,带文档协作和状态和流程
    engineercms支持文档协作和文档流程,基于flow
  • 原文地址:https://www.cnblogs.com/yansg/p/12541166.html
Copyright © 2011-2022 走看看