zoukankan      html  css  js  c++  java
  • SpringCloud学习Eureka作服务注册中心

    springcloud Netflix Eureke

    在服务注册与发现中,有一个注册中心,当服务器启动时,会把当前自己的服务器信息,如服务地址通讯地址等以别名的方式注册到注册中心,服务消费者通过别名的方式从注册中心获取实际调用地址;相当于键值对(key:别名、服务名,value:服务器元数据,如地址、端口、请求路径等);
    Eureka采用CS的设计模式,即客户端和服务端Eureka server作为服务端(使用@EnableEurekaServer注解标识),它是服务注册中心而其它微服务作为客户端(使用@EnableEurekaClient标识)注册进Eureka server维持心跳,服务消费者通过RestTemplate(使用@LoadBalanced注解来开启通过微服务名调用,并开启负载均衡)对象调用服务生产者;

    Eurka 保证 AP

    Eureka Server 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而 Eureka Client 在向某个 Eureka 注册时,如果发现连接失败,则会自动切换至其它节点。只要有一台 Eureka Server 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。
    Eurka 集群架构如下

    Eureka server通过相互注册来构建集群、Eureka Client通过注册多个同服务名的微服务进所有的Eureka server来构建集群;

    Eureka集群配置

    server:
      port: 8763
    
    eureka:
      instance:
        #hostname: localhost
        hostname: eureka8763.com
      client:
        registerWithEureka: false
        fetchRegistry: false
        serviceUrl:
          #单机
          #defaultZone:http://${eureka.instance.hostname}:${server.port}/eureka/
          #集群
          defaultZone: http://eureka8762.com:8762/eureka,http://eureka8761.com:8761/eureka
    

    Eureka Client集群配置

    spring:
      application:
        name: user-server #多个
    eureka:
      client:
        serviceUrl: #注册服务到eureka集群
          defaultZone: http://eureka8761.com:8761/eureka,http://eureka8762.com:8762/eureka,http://eureka8763.com:8763/eureka
    

    快速搭建一个Eureka Demo
    Eureka Server
    pom.xml

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

    主启动类

    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaServerApp {
        public static void main(String[] args) {
            new SpringApplicationBuilder(EurekaServerApp.class).web(WebApplicationType.SERVLET).run(args);
        }
    }
    

    application.yml

    server:
      port: 8763 #端口
    
    eureka:
      instance:
        #hostname: localhost  #服务名
        hostname: eureka8763.com  #服务名
      client:
        registerWithEureka: false #不注册自身
        fetchRegistry: false #要不要去注册中心获取其他服务的地址
        serviceUrl:
          #单机
          #defaultZone:http://${eureka.instance.hostname}:${server.port}/eureka/
          #集群
          defaultZone: http://eureka8762.com:8762/eureka,http://eureka8761.com:8761/eureka
    

    Eureka Client
    消费者
    pom.xml

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

    AdminApp.java

    /**
     * @author :jty
     * @date :20-6-11
     * @description :eureka服务集群2
     */
    @SpringBootApplication
    @EnableEurekaClient
    public class AdminApp {
        public static void main(String[] args) {
            SpringApplication.run(AdminApp.class);
        }
    }
    

    application.yml

    server:
      port: 8002
    spring:
      application:
        name: admin-server
    
    mybatis:
        ### xml存放路径
      mapper-locations: classpath:mapper/*Mapper.xml
    
    eureka:
      client:
        serviceUrl: #注册服务到eureka集群
          defaultZone: http://eureka8761.com:8761/eureka,http://eureka8762.com:8762/eureka,http://eureka8763.com:8763/eureka
    

    AdminController.java

    /**
     * @author :jty
     * @date :20-7-28
     * @description : 管理员模块
     */
    @RestController
    public class AdminController {
        @Autowired
        RestTemplate restTemplate;
    //    private static final String USER_MODULE_URL = "http://localhost:8001";
        private static final String USER_MODULE_URL = "http://user-server";
    
        @GetMapping(value = "/admin/get/user/{userId}", produces = "application/json;charset=utf-8")
        public Result searchUser(@PathVariable int userId) {
            Result result = restTemplate.getForObject(USER_MODULE_URL + "/get/user/" + userId, Result.class);
            return result;
        }
    
        @GetMapping(value = "/admin/post/create/user", produces = "application/json;charset=utf-8")
        public Result createUser(@RequestBody User user) {
            Result result = restTemplate.postForObject(USER_MODULE_URL + "/post/create/user", user, Result.class);
            return result;
        }
    }
    

    RestTemplateConfig.java

    /**
     * @author :jty
     * @date :20-7-28
     * @description :注入RestTemplate Bean
     */
    @Configuration
    public class RestTemplateConfig {
        /**
         * @LoadBalanced 通过服务名调用,开启负载均衡
         * */
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate(){
            return new RestTemplate();
        }
    }
    

    Eureka Client
    服务生产者
    pom.xml

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

    UserApp.java

    /**
     * @author :jty
     * @date :20-7-20
     * @description :用户模块   @EnableEurekaClient eureka客户端 @EnableDiscoveryClient开启服务发现,获取服务信息
     */
    @SpringBootApplication
    @EnableEurekaClient
    @EnableDiscoveryClient
    public class UserApp {
        public static void main(String[] args) {
            new SpringApplicationBuilder(UserApp.class).web(WebApplicationType.SERVLET).run(args);
        }
    }
    

    application.yml

    server:
      port: 8001
    spring:
      application:
        name: user-server #服务名
    eureka:
      client:
        serviceUrl: #注册服务到eureka集群
          defaultZone: http://eureka8761.com:8761/eureka,http://eureka8762.com:8762/eureka,http://eureka8763.com:8763/eureka
    #数据库配置略
    

    UserController.java

    /**
     * @author :jty
     * @date :20-7-20
     * @description :用户模块
     */
    @RestController
    public class UserController {
        @Autowired
        private UserMapper userMapper;
        @Autowired
        private DiscoveryClient discoveryClient;
        @Value("server.port")
        String serverPort;
        Logger logger = LoggerFactory.getLogger(UserController.class);
    
        /**
         * 查询用户
         */
        @GetMapping(value = "/get/user/{userId}", produces = "application/json;charset=utf-8")
        public Result searchUser(@PathVariable(value = "userId") int userId) {
            User user = userMapper.getUserById(userId);
            if (user != null) {
                return new Result(200, "成功" + serverPort, user);
            }
            return new Result(-200, "无数据");
        }
    
        /**
         * 添加用户
         */
        @PostMapping(value = "/post/create/user", produces = "application/json;charset=utf-8")
        public Result createUser(@RequestBody User user) {
            int i = userMapper.createUser(user);
            if (i > 0) {
                return new Result(200, "成功" + serverPort, i);
            }
            return new Result(-200, "插入失败");
        }
    
        /**
         * 服务发现
         */
        @GetMapping(value = "/get/user/discovery")
        public Object discovery() {
            List<String> services = discoveryClient.getServices();
            List<ServiceInstance> instances = discoveryClient.getInstances("user-server");
            for (String sv : services) {
                logger.info("------------>service{}", sv);
            }
            for (ServiceInstance instance : instances) {
                logger.info("-->{}---{}---{}---{}<--", instance.getInstanceId(), instance.getHost(), instance.getPort(), instance.getUri());
            }
            return instances;
        }
    

    测试

    服务监控

    服务调用

    微服务信息完善
    父工程pom.xml

    ...
    <build>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <filtering>true</filtering>
                </resource>
            </resources>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-resources-plugin</artifactId>
                    <configuration>
                        <delimiters>
                            <delimit>@</delimit>
                        </delimiters>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    ...
    

    application.yml

    ...
    eureka:
      client:
        serviceUrl: #注册服务到eureka集群
          defaultZone: http://eureka8761.com:8761/eureka,http://eureka8762.com:8762/eureka,http://eureka8763.com:8763/eureka
      instance:
        instance-id: ${spring.application.name}:${server.port} #微服务信息提示为服务名:端口,可自定义
        prefer-ip-address: true     #访问路径可以显示IP地址
    
    #微服务信息
    info:
      app.name: admin-server
      company.name: www.jty.com
      build.artifactId: @project.artifactId@
      build.version: @project.version@
    ...
    
    提示信息

    详细信息

    Eureka自我保护

    Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%(eureka.server.renewal-percent-threshold=0.85),Eureka Server 会将这些实例保护起来,让这些实例不会过期,但是在保护期内如果服务刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等;即一个服务不可用后,Eureka不会立即对其清理,依旧会对微服务的信息进行保存。

    Erueka server配置相关属性

    #将IP注册到Eureka Server上,如果不配置就是机器的主机名
    eureka.instance.prefer-ip-address=true
    
    #设为false,关闭自我保护
    eureka.server.enable-self-preservation=false
    
    #表示是否将自己注册到Eureka Server,默认为true
    eureka.client.register-with-eureka=false
    
    #表示是否从Eureka Server获取注册信息,默认为true
    eureka.client.fetch-registry=false
    
    # 扫描失效服务的间隔时间(单位毫秒,默认是60*1000)即60秒
    eureka.server.eviction-interval-timer-in-ms=5000
    
    #设置 eureka server同步失败的等待时间 默认 5分
    #在这期间,它不向客户端提供服务注册信息
    eureka.server.wait-time-in-ms-when-sync-empty=5
    
    #设置 eureka server同步失败的重试次数 默认为 5 次
    eureka.server.number-of-replication-retries=5
    
    #自我保护系数(默认0.85)
    eureka.server.renewal-percent-threshold=0.49
    
    Eureka心跳、下线、自我保护
    参考 https://blog.csdn.net/hry2015/article/details/78245149

    服务心跳

    服务实例会通过心跳(eureka.instance.lease-renewal-interval-in-seconds定义心跳的频率,默认值为30s)续约的方式向Eureka Server定时更新自己的状态。Eureka Server收到心跳后,会通知集群里的其它Eureka Server更新此实例的状态。Service Provider/Service Consumer也会定时更新缓存的实例信息。

    服务下线和剔除

    服务的下线有两种情况:

    在Service Provider服务shutdown的时候,主动通知Eureka Server把自己剔除,从而避免客户端调用已经下线的服务。
    Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认值为0,默认情况不删除实例)进行检查,如果发现实例在在一定时间(此值由eureka.instance.lease-expiration-duration-in-seconds定义,默认值为90s)内没有收到心跳,则会注销此实例。
    这种情况下,Eureka Client的最多需要[eureka.instance.lease-renewal-interval-in-seconds + eureka.client.registry-fetch-interval-seconds]时间才发现服务已经下线。同理,一个新的服务上线后,Eureka Client的服务消费方最多需要相同的时间才发现服务已经上线

    服务下线,同时会更新到Eureka Server其他节点和Eureka client的缓存,流程类似同以上的register过程

    自我保护模式

    如果Eureka Server最近1分钟收到renew的次数小于阈值(即预期的最小值),则会触发自我保护模式,此时Eureka Server此时会认为这是网络问题,它不会注销任何过期的实例。等到最近收到renew的次数大于阈值后,则Eureka Server退出自我保护模式。

    自我保护模式阈值计算:

    每个instance的预期心跳数目 = 60/每个instance的心跳间隔秒数
    阈值 = 所有注册到服务的instance的数量的预期心跳之和 *自我保护系数
    以上的参数都可配置的:

    instance的心跳间隔秒数:eureka.instance.lease-renewal-interval-in-seconds
    自我保护系数:eureka.server.renewal-percent-threshold
    如果我们的实例比较少且是内部网络时,推荐关掉此选项。我们也可以通过eureka.server.enable-self-preservation = false来禁用自我保护系数

  • 相关阅读:
    文佳夹之删除
    猜谜小游戏
    python小知识点
    【bzoj4516】[Sdoi2016]生成魔咒 后缀数组+倍增RMQ+STL-set
    【bzoj3362/3363/3364/3365】[Usaco2004 Feb]树上问题杂烩 并查集/树的直径/LCA/树的点分治
    【poj1741】Tree 树的点分治
    【bzoj2946】[Poi2000]公共串 后缀数组+二分
    【bzoj2157】旅游 树链剖分+线段树
    【bzoj2743】[HEOI2012]采花 树状数组
    【bzoj2705】[SDOI2012]Longge的问题 欧拉函数
  • 原文地址:https://www.cnblogs.com/jinit/p/13401410.html
Copyright © 2011-2022 走看看