• SpringCloud 学习笔记(3)注册中心Eureka


    Spring-Cloud 学习笔记-(3)注册中心Eureka

    1、前言

    1.1、上个章节我们做了什么?

    • user-service:作为服务提供者对外提供一个借口,根据用户id 查询用户基本信息

    • order-service:作为服务调用者,通过RestTemplate远程调用user-service

      流程如下:

    2.1、本章节我们讲会做什么?

    服务的注册与发现

    2、什么是Eureka

    2.1、问题分析

    在上一章的案例中,user-service对外提供服务,需要对外暴露自己的地址。而order-service(调用者)需要记录服务提供者的地址。将来地址出现变更,还需要及时更新。这在服务较少的时候并不觉得有什么,但是在现在日益复杂的互联网环境,一个项目肯定会拆分出十几,甚至数十个微服务。此时如果还人为管理地址,不仅开发困难,将来测试、发布上线都会非常麻烦,这与DevOps的思想是背道而驰的。

    2.2、滴滴打车

    这就好比是 网约车出现以前,人们出门叫车只能叫出租车。一些私家车想做出租却没有资格,被称为黑车。而很多人想要约车,但是无奈出租车太少,不方便。私家车很多却不敢拦,而且满大街的车,谁知道哪个才是愿意载人的。一个想要,一个愿意给,就是缺少引子,缺乏管理啊。

    此时滴滴这样的网约车平台出现了,所有想载客的私家车全部到滴滴注册,记录你的车型(服务类型),身份信息(联系方式)。这样提供服务的私家车,在滴滴那里都能找到,一目了然。

    此时要叫车的人,只需要打开APP,输入你的目的地,选择车型(服务类型),滴滴自动安排一个符合需求的车到你面前,为你服务,完美!

    2.3、Eureka做什么?

    Eureka就好比是滴滴,负责管理、记录服务提供者的信息。服务调用者无需自己寻找服务,而是把自己的需求告诉Eureka,然后Eureka会把符合你需求的服务告诉你。

    同时,服务提供方与Eureka之间通过心跳机制进行监控,当某个服务提供方出现问题,Eureka自然会把它从服务列表中剔除。

    这就实现了服务的自动注册、发现、状态监控。

    3、原理

    3.1、架构图

    该图片来自于Eureka开源代码的文档,地址为https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance

    从图上可以看得出Eureka主要有两个角色:Eureka-Server(服务端)和Eureka-Client(客户端),而客户端又可以分为ApplicationService(服务的提供者)和ApplicationClient(服务的调用者)

    3.2、服务注册于发现:

    Eureka-Client向Eureka-Server注册,并将自己的信息以key,value形式(key:serviceId,value:ip地址、端口等信息)注册到Eureka-Server,Eureka-Server再同步给其他Eureka-Server,Eureka-Client会每间接一段时间(默认30秒)向Eureka-Server发送一次心跳来表明自己还活着,如果客户端不能正常续约,他会在一定时间内(默认90秒)从服务器注册列表中剔除,然后服务器会复制更新到其他Eureka-Server集群,当ApplicationClient想要调用ApplicationService的方法时,会向EurekaServer拉取注册列表,通过serviceId找到ip和端口进行远程调用,所以保证每个区域内的Eureka-Server集群至少有一个Eureka-Server能够正常运行,防止服务器瘫痪。

    4、快速入门

    4.1、编写EurekaServer

    新建一个Module作为EurekaServer:

    我们同样选择Maven工程,方法跟之前的一样

    4.1.1、pom文件

    <?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">
        <parent>
            <artifactId>cloud-demo</artifactId>
            <groupId>com.bigfly</groupId>
            <version>1.0.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>eureka-server</artifactId>
    
        <dependencies>
            <!-- eureka-server -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
            
        </dependencies>
    
    </project>
    

    4.1.2、文件结构

    4.1.3、代码编写

    EurekaServerApplication启动类:

    package com.bigfly;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @SpringBootApplication
    @EnableEurekaServer//启动Eureka-Server
    public class EurekaServerApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(EurekaServerApplication.class);
        }
    
    }
    

    yml文件:

    server:
      port: 8761
    

    4.1.4、测试

    启动服务

    访问:http://localhost:8761/

    控制台报错:

    报错分析:

    为了保证服务的可用性Eureka-Server以后将会集群搭建,所以Eureka-Server也作为一个客户端向其他的Eureka-Server注册,这里是因为Eureka-Server找不到注册中心地址所以报错,所以我们需要在yml文件中配置注册中心地址

    解决:

    yml文件增加

    eureka:
      instance:
        hostname: 127.0.0.1
      client:
        #自己不注册自己
        registerWithEureka: false
        #不需要检索服务信息
        fetchRegistry: false
        serviceUrl:
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    

    如果页面出现如下错误,没有关系,这是因为Eureka-Server发现长时间没有客户端注册进来, Eureka Server在运行期间,会统计心跳失败的比例在15分钟之内是否低于85%,如果出现低于的情况(在单机调试的时候很容易满足,实际在
    生产环境上通常是由于网络不稳定导致),Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告。保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)

    4.2、将客户端注册到Eureka-Server

    4.2.1、user-service代码修改

    pom文件增加:

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

    application.yml文件增加:

    #服务名称
    spring:
      application:
        name: user-service
    
    # 注册中心
    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:8761/eureka
    

    4.2.2、order-service代码修改

    pom文件增加:

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

    application.yml文件增加:

    #服务名称
    spring:
      application:
        name: order-service
    
    # 注册中心
    eureka:
      client:
        service-url:
          defaultZone: http://127.0.0.1:8761/eureka
    

    4.2.3、测试

    先启动eureka-server服务,启动完毕再启动user-service和order-service

    访问http://127.0.0.1:8761 效果:

    4.3、order-service 调用user-service

    4.3.1、修改代码

    OrderServiceImpl订单服务实现类:

    package com.bigfly.service.impl;
    
    import com.bigfly.entity.Order;
    import com.bigfly.service.OrderService;
    import com.bigfly.utils.JsonUtils;
    import com.fasterxml.jackson.databind.JsonNode;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    import org.springframework.stereotype.Service;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.List;
    import java.util.UUID;
    
    @Service
    public class OrderServcieImpl implements OrderService {
    
        @Autowired
        private RestTemplate restTemplate;
    
        //↓↓↓↓↓↓↓↓↓↓↓↓修改部分↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
        @Autowired
        private DiscoveryClient discoveryClient;
        //↑↑↑↑↑↑↑↑↑↑↑↑修改部分↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
        
        /**
         * 根据用户id 查询用户信息
         * @param userId 用户id
         * @return
         */
    
    
        @Override
        public Order findById(int userId) {
    
            //↓↓↓↓↓↓↓↓↓↓↓↓修改部分↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
            //通过serviceId 拉取服务列表
            List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
            ServiceInstance instance = instances.get(0);
            //getForObject 第一个参数url代表访问路径 第二个参数代表 返回值类型
            String jsonStr = restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/api/v1/user/2", String.class);
    //        String jsonStr = restTemplate.getForObject("http://127.0.0.1:8771/api/v1/user/2", String.class);
            //↑↑↑↑↑↑↑↑↑↑↑↑修改部分↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
    
            JsonNode jsonNode = JsonUtils.str2JsonNode(jsonStr);
            Order order = new Order();
            order.setOrderName("我是一个订单");
            order.setSerialId(UUID.randomUUID().toString());
            order.setUserName(jsonNode.get("data").get("name").textValue());
            order.setPort(jsonNode.get("data").get("port").textValue());
            return order;
        }
    }
    

    4.3.2、断点调式

    1. 复制一个user-service模拟集群

    1. 启动服务

      先启动eureka-server,启动完毕后启动其他服务,其中order-service断点模式启动

    1. 调试

      访问:http://localhost:8781/api/v1/order/2

      进入断点,F8下一步

    放开断点访问结果

    5、Eureka详解

    5.1、高可用的Eureka

    模拟多个eureka-server相互注册

    修改eureka-server配置文件application.yml

    server:
      port: 8761
    
    eureka:
      instance:
        hostname: 127.0.0.1
      client:
        #自己不注册自己
    #    registerWithEureka: false
        #不需要检索服务信息
    #    fetchRegistry: false
        serviceUrl:
          defaultZone: http://127.0.0.1:8762/eureka
    
    spring:
      application:
        name: eureka-server
    

    复制一个eureka-server,方法同上

    此配置表示,端口号为8761的eureka会注册到8762上,端口号为8762的eureka会注册到8761上,因为eureka会把自己的注册列表复制更新到其他的注册中心上,所以我们最后访问 http://127.0.0.1:8761/eureka 和http://127.0.0.1:8762/eureka我们将看到这样的结果

    5.2、服务的调用方和提供方配置详解

    一个服务既可以作为服务的提供方,也可以作为服务的调用方,这里方便理解我们分开说

    1. 一个服务的提供方(比如:user-service),在服务一启动时会检查eureka.client.register-with-erueka=true属性是否为true(不配置默认是true),如果是true就会把自己注册到注册中心(eureka-server)去,反之,如果为false就不会注册自己。

    2. 在服务注册完毕过后,服务的提供方会跟注册中心维持一个心跳(没间隔一段时间向注册中心发送一次rest请求)来告诉注册中心“我还活着”,这里我们叫服务的续约(renew),如果超过一段时间(默认是90秒)注册中心还没有收到心跳注册中心就会认为此服务已经宕机,然后把这个服务从注册列表中剔除。

      eureka:
        instance:
        	#服务的续约时间间隔
        	lease-renewal-interval-in-seconds: 30
        	#服务的失效时间间隔
          lease-expiration-duration-in-seconds: 90
      

      如果在开发阶段我们可以适当把这个值跳小一点,在生产环境中这个值尽量不用修改

    3. 服务的调用方(比如:order-service),在启动时候会检测eureka.client.fetch-registry=true属性是否为true(不配置同样是true),如果是true,就会把注册中心(比如:eureka-server)的服务列表拉到本地缓存,并且没间隔一段时间(默认30秒)会重新拉取更新一次,每次调用都是从自己缓存中获取注册列表,后期如果注册中心挂了,服务依旧可以调用,但是不会更新注册列表了,所以注册中心尽量集群搭建,确保服务的可用性。

      eureka:
        client:
        	#拉去列表时间间隔
          registry-fetch-interval-seconds: 30
      

      同样,生成环境我们不用修改,开发环境我们可以修改小一点。

    5.3、失效剔除和自我保护

    1. 注册中心(eureka-server)会每间隔一段时间(默认60秒)把失效的服务(90秒没有收到心跳)从注册列表中剔除,也就是说一个服务,如果注册中心90秒没有收到心跳回复,不会立即剔除,而是每间隔一段时间统一剔除。
    2. 注册中心(eureka-server)会统计最近15分钟每分钟续约量是否超过85%,如果低于85%,eureka就会认为你没有宕机,可能是因为网络延迟等其他原因,eureka就会开启保护机制,这些实例就会被eureka保护起来,就算没有续约也不会剔除。我们在开发过程中很容易就满足续约量低于85%,所以一般开发时候会关闭自我保护(true:开启,false:关闭)
    eureka:
      server:
      	# 扫描失效服务的间隔时间(缺省为60*1000ms)
        eviction-interval-timer-in-ms: 60000 
      	# 关闭自我保护模式(缺省为打开)
        enable-self-preservation: true 
    

    5.4、小知识

    强制关闭某个服务:put:{eureka_ip:eureka_port}/eureka/apps/{appname}/{service_id}/status?value={UP/DOWN}

  • 相关阅读:
    数据库事务的四大特性
    MySQL数据库高可用性架构
    java中几种访问修饰符
    zookeeper的leader选举
    zookeeper的集群部署步骤
    MySQL索引设计原则
    SpringMVC框架知识点详解
    Spring框架知识点详解
    JAVA之DAY1
    JDK
  • 原文地址:https://www.cnblogs.com/bigfly277/p/10092582.html
Copyright © 2020-2023  润新知