服务注册发现:Eureka-入门

本篇发布于2019-12-08 20:32:21

Eureka解决了什么问题

这个问题本质是问服务注册中心的价值。很多年前,那个时候微服务还没有兴起,大家用Nginx+Tomcat就搞定了,加机器加Tomcat时候,直接在线改Nginx配置就搞定,这叫手动服务注册。但是在现今的微服务架构中,服务可能动不动就几十个,上百个,算上冗余就几百个,而且微服务的发布很频繁,不断有新的服务加入进来;在应对大流量场景时,还需要扩容缩容。这如果还依靠Nginx那种原始的手工配置方式,是不可行的,成本太高了。所以,就出现了服务注册发现,即服务实例自己会在启动后注册到服务注册中心,这叫服务注册,然后每个服务实例又从服务注册中心拉取服务注册表,动态的感知到服务的上下线,这叫服务发现。所以,服务注册中心这种技术是在微服务架构中自然而然就过渡过来的,并不突兀。eureka作为Netflix全家桶中的服务注册中心实现,其意义也就不言自明了。

快速体验Eureka

1、构建一个Eureka Server

创建一个maven工程,加入如下配置:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.1.RELEASE</version>
</parent>

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <spring.cloud-version>Hoxton.RELEASE</spring.cloud-version>
</properties>

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

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

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring.cloud-version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

创建一个application.yml文件,配置如下内容:

server:
  port: 8761

eureka:
  client:
    register-with-eureka: false #1
    fetch-registry: false       #2

#1 不要将此服务注册到eureka。因为当前服务已经是eureka server了

#2 不要拉取服务注册表。

创建一个启动类:

@Slf4j
@EnableEurekaServer
@SpringBootApplication
public class EurekaServer {

    public static void main(String[] args) {
        SpringApplication.run(EurekaServer.class, args);
        log.info("eureka server 启动成功");
    }
}

启动后,见到如下输出即为成功:
/eureka-server启动成功

2、开发一个服务提供者

@RestController
public class ServiceAController {

    @GetMapping("/sayHello/{name}")
    public String sayHello(@PathVariable("name") String name) {
        return "hello," + name;
    }
}
@Slf4j
@EnableEurekaClient
@SpringBootApplication
public class ServiceAApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceAApplication.class, args);
        log.info("ServiceA 启动成功");
    }
}

创建一个application.yml文件,配置如下内容:

server:
  port: 8080

spring:
  application:
    name: serviceA

# eureka client config
eureka:
  instance:
    hostname: loacalhost
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

服务A启动成功

3、开发一个服务调用者

@RestController
@Configuration
public class ServiceBController {

    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/greeting/{name}")
    public String greeting(@PathVariable("name") String name) {
        return restTemplate.getForObject("http://serviceA/sayHello/" + name, String.class);
    }
}
@Slf4j
@EnableEurekaClient
@SpringBootApplication
public class ServiceBApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServiceBApplication.class, args);
        log.info("ServiceB 启动成功");
    }
}

创建一个application.yml文件,配置如下内容:

server:
  port: 9000

spring:
  application:
    name: serviceB

# eureka client config
eureka:
  instance:
    hostname: loacalhost
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

启动服务

服务B启动成功

4、进行实验

浏览器访问localhost:8761打开Eureka注册服务控制台,可发现ServiceA和B都注册到Eureka Server上了

Eureka控制台
在浏览器访问http://localhost:9000/greeting/jack,就可以看到结果了

结果

5、画图讲解Hello Word原理

  • 服务A和B的启动类上都有@EnableEurekaClient注解,此注解会将服务变为Eureka客户端应用,将自己注册到Eureka Server上
  • 服务B和服务A都会从Eureka Server拉取服务注册表
  • 服务B发起调用服务A的请求时,Ribbon根据拉取到本地的服务注册表,将服务名替换为hostname+port并发起服务调用
    eureka-helloword原理

Eureka集群实战

1、eureka注册中心集群

eureka如此重要的服务注册中心,肯定不可能是单点的。下面我们来构建一个eureka server集群。

分别使用如下配置各启动一个eureka server,组成一个集群。

server:
  port: 8762

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://peer2:8761/eureka
  instance:
    hostname: peer2
server:
  port: 8761

eureka:
  client:
    register-with-eureka: false
    fetch-registry: false
    service-url:
      defaultZone: http://peer2:8762/eureka
  instance:
    hostname: peer1

小技巧:可以同一个工程,分别启动一次即可。

如果是IDEA的话,可能不让你同时启动两个,按照如下配置一下即可,在Allow parallel run前打上勾即可
idea启动多个实例
随意访问一个eureka控制台,可发现两个节点已经组成集群。DS Replicas指的是当前eureka server的副本
eureka集群

小提示:假如访问peer2节点,会发现没有服务实例。不用着急,下面我们改造后就OK了。

2、将服务改造为注册到eureka server集群

分别将服务A和B的配置改为如下这样,变更部分为最后一行

eureka:
  instance:
    hostname: localhost
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka,http://localhost:8762/eureka

改完重启后访问eureka控制台页面即可看到都有服务实例了。

服务健康自检机制

默认情况下,你的所有服务,比如服务A和服务B,都会自动给eureka注册中心同步心跳,续约。如果eureka server一段时间内没有感知到某个服务的心跳,就会把那个服务下线。

1、基于spring-boot-actuator的服务健康检查

服务加入如下依赖

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

重启后访问http://localhost:port/actuator/health可以看到返回的服务状态,默认是UP

2、spring-cloud-eureka整合spring-boot-actuator实现自定义服务健康检查

/**
 * 自定义健康检查器
 */
@Component
public class ServiceAHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        // 此处可以加上一些失败条件判断,符合这些条件就返回Status.DOWN即可
        // 主要是自己依赖的一些基础设施,看他们是否挂了或者自己连不上他们了,来决定自己是否健康
        return new Health.Builder(Status.UP).build();
    }
}
/**
 * eureka整合actuator实现自定义健康检查器
 */
@Component
public class ServiceAHealthCheckHandler implements HealthCheckHandler {

    @Autowired
    private ServiceAHealthIndicator indicator;

    @Override
    public InstanceInfo.InstanceStatus getStatus(InstanceInfo.InstanceStatus currentStatus) {
        Status status = indicator.health().getStatus();
        // 可以根据status来决定返回什么实例状态
        return InstanceInfo.InstanceStatus.UP;
    }
}

eureka client会定时调用getStatus方法来判断服务实例的状态,如果状态变化了,就会通知eureka server。当服务实例挂掉了,那么eureka server就会感知到,然后下线这个服务实例。

3、生产经验分享

一般情况下很少使用这第二种方式自定义健康状态检查器,原因如下:

  • 对于eureka
    eureka默认是client通过心跳与eureka server保持心跳通信,如果心跳不及时或者没有心跳了,那么就说明那个服务挂了,然后eureka server就会摘除这个服务实例,这个机制足够用了
  • 对于服务
    在大规模部署中,每个服务都很复杂,不可能去一个个搞一堆健康检查的。大部分情况下,我们就是会对外暴露一个/health接口,然后专门外部定时调用各个服务的/health接口来判断当前这个服务状态就足够了。

Eureka常见配置

心跳检测

eureka客户端,默认每隔30秒发送一次心跳到eureka server(以后简称注册中心)。

eureka:
  instance:
    # 配置eureka client与注册中心心跳间隔时间,默认是30秒。在eureka中,心跳被称为续约(renew)
    lease-renewal-interval-in-seconds: 
    # 配置如果多少秒内没有收到一个服务实例的心跳,就摘除(expiration 失效)这个服务,默认是90秒。
    lease-expiration-duration-in-seconds: 

一般情况下,这俩参数建议不要修改

注册表抓取

默认情况下,客户端每隔30秒去注册中心抓取最新的注册表,然后缓存到本地。通过下面参数可修改时间间隔

eureka:
  client:
    # 客户端每隔多少秒去注册中心抓取最新注册表,默认30秒
    registry-fetch-interval-seconds:

自定义元数据

通过下面的metadata-map定义服务的元数据,反正就是你自己需要的一些东西,不过一般挺少使用的

eureka:
  instance:
    metadata-map: 

自我保护模式

如果在eureka控制台看到下面这样的东西:

eureka自我保护机制

这就是eureka进入了自我保护模式。如果客户端的心跳失败超过了一定的比例,或者说在一定时间内(默认15分钟)接收到的服务续约低于85%,那么就会认为是自己网络故障了,这个时候会导致人家client无法发送心跳,如果不加以控制,eureka服务实例摘除机制会将实例一个个的下掉。为了避免这种情况的发生,eureka这个时候会一种保护模式,保护服务注册表,不会立即把失效的服务实例摘除。在测试的时候一般会关闭这个自我保护模式:

eureka:
  server:
    enable-self-preservation: false 

总结

本篇博客,我们已经初步体会如何使用eureka,剖析了eureka的常见配置,后续我们马上进入源码剖析环节。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值