精华内容
下载资源
问答
  • Eureka 服务注册发现原理剖析

    千次阅读 2020-03-27 15:33:38
    1、介绍 Eureka是Netflix开发的服务发现框架,本身是...SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务注册发现的功能。 Eureka包含两个组件:Eureka Server和Eureka Client。 Eu...

    1、介绍

    Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务。主要用于定位运行在 AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务注册与发现的功能。

    Eureka包含两个组件:Eureka Server和Eureka Client。

    Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样 EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
    Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。 在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
    Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。 综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。

    官方文档:https://github.com/Netflix/eureka/wiki/Eureka-REST-operations

    架构图
    在这里插入图片描述

    2、Eureka 环境搭建

    2.1 搭建 eureka 服务端

    引入依赖

     <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
     </dependency>
    

    父pom中引入spring-cloud-dependencies

     <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Greenwich.SR3</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    

    在主启动类上添加@EnableEurekaServer注解

    @SpringBootApplication
    @EnableEurekaServer
    public class ServiceEurekaApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ServiceEurekaApplication.class, args);
        }
    
    }
    

    编写配置文件application.properties

    #eureka服务端应用的端口默认是8761
    server.port=8761
    #表示是否将自己注册到Eureka Server,默认为true,由于当前应用就是Eureka Server,故而设为false
    eureka.client.register-with-eureka=false
    # 表示是否从Eureka Server获取注册信息,默认为true,如果是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,就设为false
    eureka.client.fetch-registry=false
    #暴露给其他eureka client 的注册地址,不配置的话默认 http://localhost:8761/eureka/
    eureka.client.service-url.defaultZone = http://www.eureka8761.com:8761/eureka/
    

    测试
    可通过 http://www.eureka8761.com:8761访问管理页
    在这里插入图片描述

    http://www.eureka8761.com:8761/apps 查看注册节点详情
    在这里插入图片描述

    2.2 搭建 eureka 客户端

    引入依赖

     <dependency>
             <groupId>org.springframework.cloud</groupId>
             <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
     </dependency>
    

    在主启动类上添加@EnableDiscoveryClient (可以不加)

    @SpringBootApplication
    //@EnableDiscoveryClient
    public class ServiceOrderApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ServiceOrderApplication.class, args);
        }
    
    }
    

    编写配置文件application.properties

    server.port=8010
    #注册到eureka服务端的微服务名称
    spring.application.name=service-order
    #注册到eureka服务端的地址
    eureka.client.service-url.defaultZone=http://www.eureka8761.com:8761/eureka/
    #将ip注册到Eureka Server上   ip替代hostname
    eureka.instance.prefer-ip-address=true
    #显示微服务的服务实例id
    eureka.instance.instance-id=service-order-${server.port}
    

    测试
    使用RestTemplate进行服务调用,可以使用微服务名称 (spring.application.name)

     String url = "http://service-order/order/findOrderByUserId/"+id;
     ResponseEntity<List> responseEntity = restTemplate.getForEntity(url,List.class);
     List<Order> orderList = responseEntity.getBody();
    

    2.3 搭建 eureka 集群

    启动两个 eureka 服务端: www.eureka8761.com:8761 www.eureka8761.com:8762。多个服务端之间要相互注册。

    8761端口配置:

    #eureka服务端应用的端口默认是8761
    server.port=8761
    #表示是否将自己注册到Eureka Server,默认为true,由于当前应用就是Eureka Server,故而设为false
    eureka.client.register-with-eureka=false
    # 表示是否从Eureka Server获取注册信息,默认为true,如果是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,就设为false
    eureka.client.fetch-registry=false
    #暴露给其他eureka client 的注册地址
    eureka.client.service-url.defaultZone = http://www.eureka8762.com:8762/eureka/
    

    8762端口配置:

    #eureka服务端应用的端口默认是8761
    server.port=8762
    #表示是否将自己注册到Eureka Server,默认为true,由于当前应用就是Eureka Server,故而设为false
    eureka.client.register-with-eureka=false
    # 表示是否从Eureka Server获取注册信息,默认为true,如果是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,就设为false
    eureka.client.fetch-registry=false
    #暴露给其他eureka client 的注册地址
    eureka.client.service-url.defaultZone = http://www.eureka8761.com:8761/eureka/
    

    eureka client需向两个注册中心注册

    #注册到eureka服务端的地址
    eureka.client.service-url.defaultZone=http://www.eureka8761.com:8761/eureka/,http://www.eureka8761.com:8761/eureka/
    

    2.4 安全配置

    eureka server端引入依赖

    <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    

    修改application.properties

    # 注册的时候需要带入用户名和密码,基本格式为 http:用户名:密码@ip:port/eureka/
    eureka.client.service-url.defaultZone= http://${spring.security.user.name}:${spring.security.user.password}@www.eureka8761.com:8761/eureka/
    #开启默认的认证
    spring.security.basic.enable=true
    spring.security.user.name=root
    spring.security.user.password=root
    

    禁用csrf(跨域请求伪造)保护,否则eureka client 会注册失败

    @Configuration
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            super.configure(http);
            http.csrf().disable();
        }
    }
    

    eureka client 修改application.properties

    eureka.client.service-url.defaultZone=http://${security.login.username}:${security.login.pass}@www.eureka8761.com:8761/eureka/
    security.login.username=root
    security.login.pass=root
    

    2.5 服务上下线监控

    在某些特定的需求下,我们需要对服务的上下线进行监控,上线或下线都进行邮件通知, Eureka 中提供了事件监听的方式来扩展。 目前支持的事件如下:

    EurekaInstanceCanceledEvent 服务下线事件
    EurekaInstanceRegisteredEvent 服务注册事件
    EurekaInstanceRenewedEvent 服务续约事件
    EurekaRegistryAvailableEvent Eureka 注册中心启动事件
    EurekaServerStartedEvent Eureka Server 启动事件

    基于 Eureka 提供的事件机制,可以监控服务的上下线过程,在过程发生中可以发送邮件来进行通知

    @Component
    public class EurekaStateChangeListener {
        @EventListener
        public void listen(EurekaInstanceCanceledEvent event) {
            System.err.println(event.getServerId() + "\t" + event.getAppName()
                    + " 服务下线 ");
        }
        @EventListener
        public void listen(EurekaInstanceRegisteredEvent event) {
            InstanceInfo instanceInfo = event.getInstanceInfo();
            System.err.println(instanceInfo.getAppName() + " 进行注册 ");
        }
        @EventListener
        public void listen(EurekaInstanceRenewedEvent event) {
            System.err.println(event.getServerId() + "\t" + event.getAppName()
                    + " 服务进行续约 ");
        }
        @EventListener
        public void listen(EurekaRegistryAvailableEvent event) {
            System.err.println(" 注册中心启动 ");
        }
        @EventListener
        public void listen(EurekaServerStartedEvent event) {
            System.err.println("Eureka Server启动 ");
        }
    }
    

    3、Eureka 核心功能点

    服务注册(register):Eureka Client会通过发送REST请求的方式向Eureka Server注册自己的服务,提供自身的元数据,比如ip地址、端口、运行状况指标的url、主页地址等信息。Eureka Server接收到注册请求后,就会把这些元数据信息存储在一个双层的Map中。

    服务续约(renew):在服务注册后,Eureka Client会维护一个心跳来持续通知Eureka Server,说明服务一直处于可用状态,防止被剔除。Eureka Client在默认的情况下会每隔30秒 (eureka.instance.leaseRenewallIntervalInSeconds)发送一次心跳来进行服务续约。 eureka.instance.lease‐renewal‐interval‐in‐seconds=30

    服务同步(replicate):Eureka Server之间会互相进行注册,构建Eureka Server集群,不同Eureka Server之间会进行服务同步,用来保证服务信息的一致性。

    获取服务(get registry):服务消费者(Eureka Client)在启动的时候,会发送一个REST请求给 Eureka Server,获取上面注册的服务清单,并且缓存在Eureka Client本地,默认缓存30秒 (eureka.client.registryFetchIntervalSeconds)。同时,为了性能考虑,Eureka Server也会维护 一份只读的服务清单缓存,该缓存每隔30秒更新一次。
    前提:eureka.client.fetch‐registry=true (默认)
    eureka.client.registry‐fetch‐interval‐seconds=30

    服务调用:服务消费者在获取到服务清单后,就可以根据清单中的服务列表信息,查找到其他服务的地址,从而进行远程调用。Eureka有Region和Zone的概念,一个Region可以包含多个Zone, 在进行服务调用时,优先访问处于同一个Zone中的服务提供者。

    服务下线(cancel):当Eureka Client需要关闭或重启时,就不希望在这个时间段内再有请求进来, 所以,就需要提前先发送REST请求给Eureka Server,告诉Eureka Server自己要下线了,Eureka Server在收到请求后,就会把该服务状态置为下线(DOWN),并把该下线事件传播出去。

    服务剔除(evict):有时候,服务实例可能会因为网络故障等原因导致不能提供服务,而此时该实例也没有发送请求给Eureka Server来进行服务下线,所以,还需要有服务剔除的机制。Eureka Server在启动的时候会创建一个定时任务,每隔一段时间(默认60秒),从当前服务清单中把超时没有续约(默认90秒, eureka.instance.leaseExpirationDurationInSeconds)的服务剔除。
    eureka.instance.lease‐expiration‐duration‐in‐seconds=90

    自我保护:既然Eureka Server会定时剔除超时没有续约的服务,那就有可能出现一种场景,网络一段时间内发生了异常,所有的服务都没能够进行续约,Eureka Server就把所有的服务都剔除了,这样显然不太合理。所以,就有了自我保护机制,当短时间内(15min是否低于85%),统计续约失败的比例,如果达到一定阈值,则会触发自我保护的机制,在该机制下,Eureka Server不会剔除任何的微服务,等到正常后,再退出自我保护机制。
    自我保护开关 (eureka.server.enableself-preservation: false)

    4、Eureka 注册中心常用http rest接口

    查看所有服务的注册列表: 
    GET http://www.eureka8761.com:8761/eureka/apps  
    
    查看某一服务的注册列表: 
    GET http://www.eureka8761.com:8761/eureka/apps/SERVICE‐NAME 
    
    暂时剔除某服务(隔几秒会重新注册上来) 
    DELETE http://www.eureka8761.com:8761/eureka/apps/SERVICE‐NAME/INSTANCE‐NAME 
    
    下线某一服务:
    PUT http://www.eureka8761.com:8761/eureka/apps/SERVICE‐NAME/INSTANCENAME/status?value=OUT_OF_SERVICE 
    
    下线后再恢复服务: 
    PUT http://www.eureka8761.com:8761/eureka/apps/SERVICE‐NAME/INSTANCE‐NAM E/ status?value=UP
    
    
    展开全文
  • 我的博客,原文出处... #Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认60s)进行检查,如果发现实例在在一定时间 #(此值由客户端设置的eureka.instance.lease-expiration-dur

    我的博客,原文出处http://www.deepinblog.com/eureka/175/

    我的博客地址,原文出处

    先直接给出配置让你尝鲜

    EurekaServer端配置

    eureka:
      server:
        #Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认60s)进行检查,如果发现实例在在一定时间
        #(此值由客户端设置的eureka.instance.lease-expiration-duration-in-seconds定义,默认值为90s)内没有收到心跳,则会注销此实例。
        #我们这里配置每秒钟去检测一次,驱除失效的实例
        eviction-interval-timer-in-ms: 1000
        #关闭一级缓存,让客户端直接从二级缓存去读取,省去各缓存之间的同步的时间
        use-read-only-response-cache: false
    

    EurekaClient端(应用端)配置

    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
        # EurekaClient每隔多久从EurekaServer拉取一次服务列表,默认30秒,这里修改为2秒钟从注册中心拉取一次
        registry-fetch-interval-seconds: 2
      #租约期限以及续约期限的配置
      instance:
        #租约到期,服务失效时间,默认值90秒,服务超过90秒没有发生心跳,EurekaServer会将服务从列表移除
        #这里修改为6秒
        lease-expiration-duration-in-seconds: 6
        #租约续约间隔时间,默认30秒,这里修改为3秒钟
        lease-renewal-interval-in-seconds: 3
    
    #这里是Ribbon缓存实例列表的刷新间隔,默认30秒钟,这里修改为每秒钟刷新一次实例信息
    ribbon:
      ServerListRefreshInterval: 1000
    

    下面给你剖析原理

    Eureka服务端详解

    服务端缓存

    如图所示

    file

    服务注册到注册中心后,服务实例信息是存储在注册表中的,也就是内存中。但Eureka为了提高响应速度,在内部做了优化,加入了两层的缓存结构,将Client需要的实例信息,直接缓存起来,获取的时候 直接从缓存中拿数据然后响应给 Client。

    第一层缓存是readOnlyCacheMap,readOnlyCacheMap是采用ConcurrentHashMap来存储数据的,主要负责定时与readWriteCacheMap进行数据同步,默认同 步时间为 30 秒一次。

    第二层缓存是readWriteCacheMap,readWriteCacheMap采用Guava来实现缓存。缓存过期时间默认为180秒,当服务下线、过期、注册、状态变更等操作都会清除此缓存中的数据。

    第三层是数据存储层。

    Client获取服务实例数据时,会先从一级缓存中获取,如果一级缓存中不存在,再从二级缓存中获取,如果二级缓存也不存在,会触发缓存的加载,从存储层拉取数据到缓存中,然后再返回给 Client。

    Eureka 之所以设计二级缓存机制,也是为了提高 Eureka Server 的响应速度,缺点是缓存会导致 Client 获取不到最新的服务实例信息,然后导致无法快速发现新的服务和已下线的服务。

    了解了服务端的实现后,想要解决这个问题就变得很简单了,我们可以缩短只读缓存的更新时间 (eureka.server.response-cache-update-interval-ms)让服务发现变得更加及时,或者直接将只读缓 存关闭(eureka.server.use-read-only-response-cache=false),多级缓存也导致Client层面(数据一致性)很薄弱。

    客户端缓存

    客户端缓存主要分为两块内容,一块是 Eureka Client 缓存,一块是 Ribbon 缓存。

    Eureka Client缓存 ,EurekaClient负责跟EurekaServer进行交互,在EurekaClient中的 com.netflix.discovery.DiscoveryClient.initScheduledTasks() 方法中,初始化了一个 CacheRefreshThread 定时任务专⻔用来拉取 Eureka Server 的实例信息到本地。所以我们需要缩短这个定时拉取服务信息的时间间隔(此值在客户端配置eureka.client.registryFetchIntervalSeconds) 来快速发现新的服务

    Ribbon缓存,Ribbon会从EurekaClient中获取服务信息,ServerListUpdater是Ribbon中负责服务实例 更新的组件,默认的实现是PollingServerListUpdater,通过线程定时去更新实例信息。定时刷新的时 间间隔默认是30秒,当服务停止或者上线后,这边最快也需要30秒才能将实例信息更新成最新的。我们可以将这个时间调短一点,比如 3 秒。

    刷新间隔的参数是通过 getRefreshIntervalMs 方法来获取的,方法中的逻辑也是从 Ribbon的配置中进行取值的。所以我们需要缩短这个更新间隔(此值在客户端配置ribbon.ServerListRefreshInterval)来快速的更新Ribbon缓存实例列表

    将这些服务端缓存和客户端缓存的时间全部缩短后,跟默认的配置时间相比,快了很多。我们通过调整 参数的方式来尽量加快服务发现的速度,但是还是不能完全解决报错的问题,间隔时间设置为3秒,也还是会有间隔。所以我们一般都会开启重试功能,当路由的服务出现问题时,可以重试到另一个服务来 保证这次请求的成功。

    服务端缓存部分源码如下:

    /**
     *The class that is responsible for caching registry information that will be
     *queried by the clients.
     */
    public class ResponseCacheImpl implements ResponseCache {
        //一级缓存
      	private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>();
        //二级缓存(Guava实现)
      	private final LoadingCache<Key, Value> readWriteCacheMap;
        //数据存储层
        private final AbstractInstanceRegistry registry;
    }
    
    

    客户端缓存更新部分源码如下:

    Eureka Client缓存刷新部分源码

        //Eureka Client缓存刷新部分源码
        /**
         * Initializes all scheduled tasks.
         * 在实例化com.netflix.discovery.DiscoveryClient被调用
         */
        private void initScheduledTasks() {
            if (clientConfig.shouldFetchRegistry()) {
                // registry cache refresh timer
                int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
                int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
                scheduler.schedule(
                        new TimedSupervisorTask(
                                "cacheRefresh",
                                scheduler,
                                cacheRefreshExecutor,
                          			//从哪个Eureka客户端拉取实例列表的间隔时间
                  							//通过eureka.client.registryFetchIntervalSeconds可配置
                                registryFetchIntervalSeconds,
                                TimeUnit.SECONDS,
                                expBackOffBound,
                          			//执行刷新的Runable定时任务
                                new CacheRefreshThread()
                        ),
                  			//从哪个Eureka客户端拉取实例列表的间隔时间
                  			//通过eureka.client.registryFetchIntervalSeconds可配置
                        registryFetchIntervalSeconds, TimeUnit.SECONDS);
            }
    
            if (clientConfig.shouldRegisterWithEureka()) {
                int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
                int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
                logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);
    
                // Heartbeat timer
                scheduler.schedule(
                        new TimedSupervisorTask(
                                "heartbeat",
                                scheduler,
                                heartbeatExecutor,
                                renewalIntervalInSecs,
                                TimeUnit.SECONDS,
                                expBackOffBound,
                                new HeartbeatThread()
                        ),
                        renewalIntervalInSecs, TimeUnit.SECONDS);
    
                // InstanceInfo replicator
                instanceInfoReplicator = new InstanceInfoReplicator(
                        this,
                        instanceInfo,
                        clientConfig.getInstanceInfoReplicationIntervalSeconds(),
                        2); // burstSize
    
                statusChangeListener = new ApplicationInfoManager.StatusChangeListener() {
                    @Override
                    public String getId() {
                        return "statusChangeListener";
                    }
    
                    @Override
                    public void notify(StatusChangeEvent statusChangeEvent) {
                        if (InstanceStatus.DOWN == statusChangeEvent.getStatus() ||
                                InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) {
                            // log at warn level if DOWN was involved
                            logger.warn("Saw local status change event {}", statusChangeEvent);
                        } else {
                            logger.info("Saw local status change event {}", statusChangeEvent);
                        }
                        instanceInfoReplicator.onDemandUpdate();
                    }
                };
    
                if (clientConfig.shouldOnDemandUpdateStatusChange()) {
                    applicationInfoManager.registerStatusChangeListener(statusChangeListener);
                }
    
                instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
            } else {
                logger.info("Not registering with Eureka server per configuration");
            }
        }	
    

    Ribbon缓存刷新部分源码

       //PollingServerListUpdater,Ribbon缓存刷新部分源码
       @Override
        public synchronized void start(final UpdateAction updateAction) {
            if (isActive.compareAndSet(false, true)) {
                final Runnable wrapperRunnable = new Runnable() {
                    @Override
                    public void run() {
                        if (!isActive.get()) {
                            if (scheduledFuture != null) {
                                scheduledFuture.cancel(true);
                            }
                            return;
                        }
                        try {
                            updateAction.doUpdate();
                            lastUpdated = System.currentTimeMillis();
                        } catch (Exception e) {
                            logger.warn("Failed one update cycle", e);
                        }
                    }
                };
    
                scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
                        wrapperRunnable,
                        initialDelayMs,
                        refreshIntervalMs,//默认30秒,通过ribbon.ServerListRefreshInterval来配置更小的值来快速更新Ribbon实例缓存
                        TimeUnit.MILLISECONDS
                );
            } else {
                logger.info("Already active, no-op");
            }
        }
    

    服务下线

    1. 当服务正常关闭操作时,会发送服务下线的REST请求给EurekaServer。
    2. 服务中心接受到请求后,将该服务置为下线状态

    失效剔除

    Eureka Server 中会有定时任务去检测失效的服务,将服务实例信息从注册表中移除,也可以将这个失效检测的时间缩短,这样服务下线后就能够及时从注册表中清除。

    1. Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认60s)进行检查,
      如果发现实例在在一定时间
      (此值由客户端设置的eureka.instance.lease-expiration-duration-in-seconds定义,默认值为90s)内没有收到心跳,
      则会注销此实例。

    Eureka客户端

    服务提供者(也是Eureka客户端)

    • 服务提供者(也是Eureka客户端)要向EurekaServer注册服务,并完成服务续约等工作
    • 服务注册详解(服务提供者)
      1. 当我们导入了eureka-client依赖坐标,配置Eureka服务注册中心地址
      2. 服务在启动时会向注册中心发起注册请求,携带服务元数据信息
      3. Eureka注册中心会把服务的信息保存在Map中。
    • 服务续约详解(服务提供者)
      1. 服务每隔30秒会向注册中心续约(心跳)一次(也称为报活),如果没有续约,租约在90秒后到期,然后服务会被失效。
        每隔30秒的续约操作我们称之为心跳检测往往不需要我们调整这两个配置
    #向Eureka服务中心集群注册服务 
    eureka:
      #租约期限以及续约期限的配置
      instance:
        #租约到期,服务失效时间,默认值90秒,服务超过90秒没有发生心跳,EurekaServer会将服务从列表移除
        #这里修改为6秒
        lease-expiration-duration-in-seconds: 6
        #租约续约间隔时间,默认30秒,这里修改为3秒钟
        lease-renewal-interval-in-seconds: 3
    

    服务消费者(也是Eureka客户端)

    • 服务消费者每隔30秒服务会从注册中心中拉取一份服务列表,这个时间可以通过配置修改。往往不需要我们调整
      1. 服务消费者启动时,从 EurekaServer服务列表获取只读备份,缓存到本地;
      2. 默认每隔30秒,会重新获取并更新数据;
      3. 每隔30秒的时间可以通过配置eureka.client.registry-fetch-interval-seconds修改,如下。
    #向Eureka服务中心集群注册服务 
    eureka:
        client:
          # EurekaClient每隔多久从EurekaServer拉取一次服务列表,默认30秒,这里修改为2秒钟从注册中心拉取一次
        	registry-fetch-interval-seconds: 2
    

    客户端缓存见Eureka服务端详情章节

    至此您应该明白Eureka的服务发现机制了吧

    最后再说说Eureka自我保护机制

    服务提供者 —> 注册中心

    • 定期的续约(服务提供者和注册中心通信),假如服务提供者和注册中心之间的网络有点问题,
      不代表 服务提供者不可用,不代表服务消费者无法访问服务提供者,所以有自我保护的机制
    • 如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,
      Eureka Server自动进入自我保护机制。
    • 为什么会有自我保护机制?
      • 默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳, Eureka Server将会移除该实例。
        但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,而微服务本身是正常运行的,此时不应该移除这个微服务,
        所以引入了自我保护机制。

    服务中心⻚面会显示如下提示信息
    file

    当处于自我保护模式时

    1. 不会剔除任何服务实例(可能是服务提供者和EurekaServer之间网络问题),保证了大多数服务依 然可用
    2. Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用,
      当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
    3. 在Eureka Server工程中通过eureka.server.enable-self-preservation配置可用关停自我保护,默认值是打开
    eureka:
      server:
        enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
    

    如果有大批量的集群且存在网络分区,强烈建议开启自我保护机制

    展开全文
  • Eureka 服务注册发现

    2020-12-19 23:27:49
    Eureka 服务注册发现


    1-Eureka 概述

    1.1-服务治理

    SpringCloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理

    在传统的 RPC 远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理。管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

    1.2-服务注册

    SpringCloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现(对比 Zookeeper)

    Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server 并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。SpringCloud 的一些其他模块(比如 Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。

    服务注册与发现中有一个注册中心,当服务器启动的时候,会把当前自己服务器的信息(比如服务地址通讯地址等)以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地 RPC 调用 RPC 远程调用。框架核心设计思想在于:注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何 RPC 远程框架中,都会有一个注册中心(存放服务地址相关信息)。

    请注意和 Dubbo 的架构对比
    在这里插入图片描述
    在这里插入图片描述

    1.3-Eureka 组件

    Eureka 包含两个组件:Eureka Server 和 Eureka Client。

    • Eureka Server 提供服务注册服务

      各个节点启动后,会在 Eureka Server 中进行注册,这样 Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。

    • Eureka Client 是一个 Java 客户端

      用于简化 Eureka Server 的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向 Eureka Server 发送心跳(默认周期为 30 秒)。如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除(默认 90 秒)。


    2-Eureka 单机

    2.1-注册中心服务 cloud-eureka-server7001

    a、右击父工程 mscloud,新建子模块 cloud-eureka-server7001
    在这里插入图片描述
    b、修改子模块 cloud-eureka-server7001 中 pom.xml
    在这里插入图片描述

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
    
        <artifactId>cloud-eureka-server7001</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
            </dependency>
        </dependencies>
    </project>
    

    c、修改子模块 cloud-eureka-server7001,目录 /src/main/resources 中新建 application.yml

    server:
      port: 7001
    
    eureka:
      instance:
        # Eureka 服务端的实例名字
        hostname: localhost
      client:
        # 标识不向注册中心注册自己
        register-with-eureka: false
        # 表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
        fetch-registry: false
        service-url:
          #设置与 Eureka Server 交互的地址查询服务和注册服务都需要依赖这个地址
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    

    d、修改子模块 cloud-eureka-server7001,目录 /src/main/java 中新建包 com.atguigu.springcloud

    e、修改子模块 cloud-eureka-server7001,包 com.atguigu.springcloud 中新建启动类 EurekaMain7001.java
    在这里插入图片描述

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @EnableEurekaServer
    @SpringBootApplication
    public class EurekaMain7001 {
        public static void main(String[] args) {
            SpringApplication.run(EurekaMain7001.class,args);
        }
    }
    

    f、测试子模块 cloud-eureka-server7001

    2.2-提供者服务 cloud-provider-payment8001 服务注册

    a、修改子模块 cloud-provider-payment8001 中 pom.xml
    在这里插入图片描述

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
    
        <artifactId>cloud-provider-payment8001</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.10</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-jdbc</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
        </dependencies>
    </project>
    

    b、修改子模块 cloud-provider-payment8001,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 8001
    
    eureka:
      client:
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
    
    spring:
      application:
        name: cloud-payment-service
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: org.gjt.mm.mysql.Driver
        url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    
    mybatis:
      mapperLocations: classpath:mapper/*.xml
      type-aliases-package: com.atguigu.springcloud.entities
    

    c、修改子模块 cloud-provider-payment8001,包 com.atguigu.springcloud 中启动类 PaymentMain8001.java
    在这里插入图片描述

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @EnableEurekaClient
    @SpringBootApplication
    public class PaymentMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8001.class, args);
        }
    }
    

    d、测试子模块 cloud-provider-payment8001 是否注册

    • 启动子模块(Eureka Server) microservicecloud-eureka-7001

    • 启动子模块(Eureka Client) cloud-provider-payment8001

    • http://localhost:7001/
      在这里插入图片描述
      微服务注册名称配置说明
      在这里插入图片描述

    2.3-消费者服务 cloud-consumer-order80 服务注册

    a、修改子模块 cloud-consumer-order80 中 pom.xml
    在这里插入图片描述

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
    
        <artifactId>cloud-consumer-order80</artifactId>
    
        <dependencies>
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>
    

    b、修改子模块 cloud-consumer-order80,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 80
    
    eureka:
      client:
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
    
    spring:
      application:
        name: cloud-order-service
    

    c、修改子模块 cloud-consumer-order80,包 com.atguigu.springcloud 中启动类 OrderMain80.java
    在这里插入图片描述

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @EnableEurekaClient
    @SpringBootApplication
    public class OrderMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderMain80.class, args);
        }
    }
    

    d、测试子模块 cloud-consumer-order80 是否注册

    2.4-Actuator 监控与服务注册信息完善

    2.4.1-主机名称:服务名称修改

    在这里插入图片描述
    a、修改子模块 cloud-provider-payment8001,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 8001
    
    eureka:
      client:
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
      instance:
        # 自定义服务名称
        instance-id: payment8001
    
    spring:
      application:
        name: cloud-payment-service
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: org.gjt.mm.mysql.Driver
        url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    
    mybatis:
      mapperLocations: classpath:mapper/*.xml
      type-aliases-package: com.atguigu.springcloud.entities
    

    b、查看效果
    在这里插入图片描述

    2.4.2-访问信息:添加 IP 信息提示

    a、修改子模块 cloud-provider-payment8001,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 8001
    
    eureka:
      client:
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
      instance:
        # 自定义服务名称
        instance-id: payment8001
        # 访问路径可以显示IP地址
        prefer-ip-address: true
    
    spring:
      application:
        name: cloud-payment-service
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: org.gjt.mm.mysql.Driver
        url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    
    mybatis:
      mapperLocations: classpath:mapper/*.xml
      type-aliases-package: com.atguigu.springcloud.entities
    

    b、查看效果

    鼠标停留在服务名称 cloud-provider-payment8001 上显示左下角地址
    在这里插入图片描述

    2.4.3-微服务 Info 详细信息

    点击服务名称超链接跳转内容
    在这里插入图片描述
    a、修改子模块 cloud-provider-payment8001,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 8001
    
    eureka:
      client:
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
      instance:
        # 自定义服务名称
        instance-id: payment8001
        # 访问路径可以显示IP地址
        prefer-ip-address: true
    
    info:
      app.name: atguigu-payment8001
      company.name: www.atguigu.com
      build.artifactId: "@project.artifactId@"
      build.version: "@project.version@"
    
    spring:
      application:
        name: cloud-payment-service
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: org.gjt.mm.mysql.Driver
        url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    
    mybatis:
      mapperLocations: classpath:mapper/*.xml
      type-aliases-package: com.atguigu.springcloud.entities
    

    b、查看效果
    在这里插入图片描述

    2.5-Eureka 自我保护机制

    2.5.1-故障现象

    保护模式主要用于一组客户端和 Eureka Server 之间存在网络分区场景下的保护。

    一旦进入保护模式,Eureka Server 将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务

    如果在 Eureka Server 的首页看到以下提示,则说明 Eureka 进入了保护模式:

    EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHENTHEY'RE NOT.
    RENEWALS ARE LESSERTHANTHRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE
    

    在这里插入图片描述

    2.5.2-导致原因

    • 为什么会产生 Eureka 自我保护机制?

      防止 Eureka Client 可以正常运行,但是与 Eureka Server 网络不通情况下,Eureka Server 不会立刻将 Eureka Client 服务剔除。

    • 什么是自我保护模式?

      默认情况下,如果 Eureka Server 在一定时间内没有接收到某个微服务实例的心跳,Eureka Server 将会注销该实例(默认 90 秒)。但是当网络分区故障发生时,微服务与 Eureka Server 之间无法正常通信,以上行为可能变得非常危险了,因为微服务本身其实是健康的,此时本不应该注销这个微服务

      Eureka 通过自我保护模式来解决这个问题,当 Eureka Server 节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server 就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该 Eureka Server 节点会自动退出自我保护模式。

    在自我保护模式中,Eureka Server 会保护服务注册表中的信息,不再注销任何服务实例

    它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着

    综上:自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让 Eureka 集群更加的健壮、稳定。一句话:某时刻某一个微服务不可用了,Eureka 不会立刻清理,依旧会对该微服务的信息进行保存。

    2.5.3-禁止自我保护

    一般生产环境中不会禁止自我保护

    默认自我保护机制是开启的(eureka.server.enable-self-preservation = true)。使用 eureka.server.enable-self-preservation = false 可以禁用自我保护模式

    a、修改子模块 cloud-eureka-server7001,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 7001
    
    eureka:
      instance:
        # Eureka 服务端的实例名字
        hostname: localhost
      client:
        # 标识不向注册中心注册自己
        register-with-eureka: false
        # 表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
        fetch-registry: false
        service-url:
          #设置与 Eureka Server 交互的地址查询服务和注册服务都需要依赖这个地址
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
      server:
        # 关闭自我保护机制:保证不可用服务被及时踢除
        enable-self-preservation: false
        eviction-interval-timer-in-ms: 2000
    

    Eureka Server 禁止自我保护效果: 在这里插入图片描述
    b、修改子模块 cloud-provider-payment8001,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 8001
    
    eureka:
      client:
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
      instance:
        # 自定义服务名称
        instance-id: payment8001
        # 访问路径可以显示IP地址
        prefer-ip-address: true
        # Eureka 客户端向服务端发送心跳的时间间隔,单位为秒(默认是 30 秒)
        lease-renewal-interval-in-seconds: 1
        # Eureka 服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是 90 秒),超时将剔除服务
        lease-expiration-duration-in-seconds: 2
    
    info:
      app.name: atguigu-payment8001
      company.name: www.atguigu.com
      build.artifactId: "@project.artifactId@"
      build.version: "@project.version@"
    
    spring:
      application:
        name: cloud-payment-service
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: org.gjt.mm.mysql.Driver
        url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    
    mybatis:
      mapperLocations: classpath:mapper/*.xml
      type-aliases-package: com.atguigu.springcloud.entities
    

    c、测试禁止自我保护

    2.6-提供者服务 cloud-provider-payment8001 服务发现

    对于注册进 Eureka 里面的微服务,可以通过服务发现来获得该服务的信息。

    a、修改子模块 cloud-provider-payment8001,包 com.atguigu.springcloud.controller 中 PaymentController.java
    在这里插入图片描述

    package com.atguigu.springcloud.controller;
    
    import com.atguigu.springcloud.entities.CommonResult;
    import com.atguigu.springcloud.entities.Payment;
    import com.atguigu.springcloud.service.PaymentService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @RestController
    @Slf4j
    public class PaymentController {
        @Resource
        private PaymentService paymentService;
        @Resource
        private DiscoveryClient discoveryClient;
    
        @PostMapping(value = "/payment/create")
        public CommonResult create(Payment payment) {
            int result = paymentService.create(payment);
            log.info("*****插入结果:" + result);
            if (result > 0) {  //成功
                return new CommonResult(200, "插入数据库成功", result);
            } else {
                return new CommonResult(444, "插入数据库失败", null);
            }
        }
    
        @GetMapping(value = "/payment/get/{id}")
        public CommonResult getPaymentById(@PathVariable("id") Long id) {
            Payment payment = paymentService.getPaymentById(id);
            log.info("*****查询结果:" + payment);
            if (payment != null) {  //说明有数据,能查询成功
                return new CommonResult(200, "查询成功", payment);
            } else {
                return new CommonResult(444, "没有对应记录,查询ID:" + id, null);
            }
        }
    
        @GetMapping(value = "/payment/discovery")
        public Object discovery() {
            List<String> services = discoveryClient.getServices();
            for (String element : services) {
                log.info("***** element:" + element);
            }
            List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
            for (ServiceInstance instance : instances) {
                log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t"
                        + instance.getPort() + "\t" + instance.getUri());
            }
            return this.discoveryClient;
        }
    }
    

    b、修改子模块 cloud-provider-payment8001,包 com.atguigu.springcloud 中启动类 PaymentMain8001.java
    在这里插入图片描述

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @EnableEurekaClient
    @EnableDiscoveryClient
    @SpringBootApplication
    public class PaymentMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8001.class, args);
        }
    }
    

    c、测试子模块 cloud-provider-payment8001 服务发现


    3-Eureka 集群

    搭建 Eureka 注册中心集群,实现负载均衡 + 故障容错

    3.1-基本原理

    服务注册:将服务信息注册进注册中心。

    服务发现:从注册中心上获取服务信息,实质:存 Key 服务名取 Value 调用地址

    流程概况

    • 启动 Eureka 注册中心(cloud-eureka-server7001)。

    • 启动服务提供者(cloud-provider-payment8001)。

    • 服务提供者启动后会把自身信息(比如服务地址以别名方式)注册进 Eureka。

    • 服务消费者(cloud-consumer-order80)在需要调用接口时,使用服务别名去注册中心获取实际的 RPC 远程调用地址。

    • 服务消费者获得调用地址后,底层实际是利用 HttpClient 技术实现远程调用。

    • 服务消费者获得服务地址后会存在本地 Jvm 内存中,默认每 30 秒更新一次服务调用地址。

    3.2-集群配置

    a、右击父工程 mscloud,新建子模块 cloud-eureka-server7002
    在这里插入图片描述
    b、复制子模块 cloud-eureka-server7001 中 pom.xml 内容到子模块 cloud-eureka-server7002 中 pom.xml

    c、修改子模块 cloud-eureka-server7002 中 pom.xml
    在这里插入图片描述
    d、修改子模块 cloud-eureka-server7002,目录 /src/main/java 中新建包 com.atguigu.springcloud

    e、复制子模块 cloud-eureka-server7001 中包 com.atguigu.springcloud 中启动类 EurekaMain7001.java 到子模块 cloud-eureka-server7002 包 com.atguigu.springcloud

    f、子模块 cloud-eureka-server7002,包 com.atguigu.springcloud 中启动类重命名为 EurekaMain7002.java

    g、修改 Windows 网络映射配置

    找到 C:\Windows\System32\drivers\etc 路径下的 hosts 文件,添加内容:

    127.0.0.1 eureka7001.com
    127.0.0.1 eureka7002.com
    

    h、修改子模块 cloud-eureka-server7001,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 7001
    
    eureka:
      instance:
        # Eureka 服务端的实例名字
        hostname: eureka7001.com
      client:
        # 标识不向注册中心注册自己
        register-with-eureka: false
        # 表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
        fetch-registry: false
        service-url:
          #设置与 Eureka Server 交互的地址查询服务和注册服务都需要依赖这个地址
          defaultZone: http://eureka7002.com:7002/eureka/
    

    i、修改子模块 cloud-eureka-server7002,目录 /src/main/resources 中新建 application.yml
    在这里插入图片描述

    server:
      port: 7002
    
    eureka:
      instance:
        # Eureka 服务端的实例名字
        hostname: eureka7002.com
      client:
        # 标识不向注册中心注册自己
        register-with-eureka: false
        # 表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
        fetch-registry: false
        service-url:
          #设置与 Eureka Server 交互的地址查询服务和注册服务都需要依赖这个地址
          defaultZone: http://eureka7001.com:7001/eureka/
    

    j、修改子模块 cloud-provider-payment8001,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 8001
    
    eureka:
      client:
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          # 单机版
          #defaultZone: http://localhost:7001/eureka
          # 集群版
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
    
      instance:
        # 自定义服务名称
        instance-id: payment8001
        # 访问路径可以显示IP地址
        prefer-ip-address: true
        # Eureka 客户端向服务端发送心跳的时间间隔,单位为秒(默认是 30 秒)
        lease-renewal-interval-in-seconds: 1
        # Eureka 服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是 90 秒),超时将剔除服务
        lease-expiration-duration-in-seconds: 2
    
    info:
      app.name: atguigu-payment8001
      company.name: www.atguigu.com
      build.artifactId: "@project.artifactId@"
      build.version: "@project.version@"
    
    spring:
      application:
        name: cloud-payment-service
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: org.gjt.mm.mysql.Driver
        url: jdbc:mysql://localhost:3306/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: 123456
    
    mybatis:
      mapperLocations: classpath:mapper/*.xml
      type-aliases-package: com.atguigu.springcloud.entities
    

    k、修改子模块 cloud-consumer-order80,目录 /src/main/resources 中 application.yml
    在这里插入图片描述

    server:
      port: 80
    
    spring:
      application:
        name: cloud-order-service
    
    eureka:
      client:
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
    

    k、测试 Eureka 集群


    展开全文
  • Eureka服务发现注册详解

    万次阅读 2019-06-25 16:48:38
    Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以...
  • 深入学习 Eureka 原理 SpringCloud 注册中心 Eureka 集群是怎么保持数据一致的? 一、简介 Eureka跟Zookeeper一样可以用作注册中心。 1.组件 1.1.1 Eureka Server 服务注册中心 1.启动后,从其他节点拉取...
  • Eureka服务注册原理

    2020-03-02 18:20:51
    原理图 相关分析 为什么不在服务注册表中进行注册和拉取 读写操作的分离
  • Eureka服务注册中心的原理 故障转移: Eureka内存实际上是有俩级缓存,一个是只读缓存,一个是可以更改的缓存。当注册表更新后,会立马同步到ReadWrite缓存,ReadWrite缓存会定时同步到ReadOnly缓存,而服务发现则...
  • 注册中心是分布式开发的核心组件之一,而eureka是spring cloud推荐的注册中心实现,因此对于Java开发同学来说,还是有必要学习eureka的,特别是其架构及设计思想。 官方文档定义是:Eureka is a REST ...
  • Eureka服务注册发现

    2020-10-17 17:07:19
    声明:此博客是在学习视频过程中的学习笔记,知识点整理,和阳哥的...服务治理就是为了解决这些问题,实现服务调用,负载均衡,容错,服务发现注册等。 什么是服务注册发现 Eureka采用了CS的设计架构,Eureka S.
  • 启动时,会调用Eureka所提供的服务注册相关方法,向Eureka服务器注册自己的信息。同时,在Eureka服务器会维护一个已注册服务列表。注册服务列表使用一个嵌套HashMap保存信息,数据结构如下: HashMap的第一层为...
  • 在前面的文章介绍了,如何使用服务注册发现组件: Eureka,并给出使用示例。本文在此基础上,将会讲解 Eureka 客户端实现的内幕,结合源码深入实现的细节,知其所以然。客户端需要重点关注以下几点: 从Eureka ...
  • 文章目录Eureka服务注册发现(一)硬编码的缺陷服务发现简介Eureka简介编写单节点的Eureka Server注册微服务Eureka服务的高可用(集群)将应用注册到集群上Github地址 Eureka服务注册发现(一) 参考:《Srping ...
  • Eureka工作原理

    万次阅读 多人点赞 2019-07-03 10:46:48
    Eureka 工作原理 上节内容为大家介绍了,注册中心 Eureka 产品的使用,以及如何利用 Eureka 搭建单台和集群的注册中心。这节课我们来继续学习 Eureka,了解它的相关概念、工作流程机制等。 Eureka 作为 Spring Cloud...
  • 转载:小码的小坑 - Spring Cloud之Eureka服务注册发现(概念原理篇)
  • Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册发现对于微服务来说是非常重要的,有了服务发现注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,305
精华内容 6,122
关键字:

eureka服务注册发现原理