Eureka

​ 服务发现与注册中心

​ 由Netflix开发的服务发现框架,本身是一个基于REST的服务

​ 主要组件

​ Eureka Serve:服务端,提供服务注册和发现的功能

​ Eureka Client:客户端,与Eureka Serve的交互,客户端启动后会自动注册到其启动时配置的地址的服务端

作用

​ 对服务进行统一管理

​ 提供服务的注册于发现,将所有的服务进行统一管理,不需要开发人员手动维护,需要使用哪个服务,直接在服务中心中获取就行

​ 实现对服务的状态管理,如果某个服务下线,会有通知,专业术语是心跳,服务提供者会定期通过http方式向Eureka刷新自身的状态,同时Eureka会将所有提供者的地址发送给消费者,并定期更新

​ 结合其他技术(Ribbon)实现了负载均衡

为什么要使用

​ 微服务的基础还是服务,将一个单体应用拆分为微服务架构的应用,其中服务之间需要相互依赖,相互调用,但是服务被部署在不同的服务器上,如果服务一旦过多,那么会出现访问的地址(接口)难以进行管理,如果有集群,还需要自行实现负载均衡,并且其中有某个服务停掉也不会接到通知,为了解决等等之类的问题,便诞生了服务发现与注册中心

入门案例

​ 分为服务端和客户端,引入Eureka都需要三步,添加依赖,配置,注解

​ 首先都需要引入SpringCloud的依赖

​ 该案例使用的SpringCloud的Hoxton.RELEASE版本,需要结合springboot2.2.x使用

 <!-- SpringCloud的依赖 做到版本管理的作用 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <!-- Spring的仓库地址 -->
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

服务端

依赖

 <!-- Eureka服务端 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

配置

server:
  port: 8080 # 端口
spring:
  application:
    name: eureka-server # 应用名称,会在Eureka中显示
eureka:
  client:
    register-with-eureka: false # 是否注册自己的信息到EurekaServer,默认是true
    fetch-registry: false # 是否拉取其它服务的信息,默认是true
    service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要加上其它Server的地址。
      defaultZone: http://127.0.0.1:${server.port}/eureka

注解

​ 在启动类上添加

@EnableEurekaServer // 声明这个应用是一个EurekaServer

访问:http://127.0.0.1:8080

客户端

依赖

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

配置

server:
  port: 8081
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8
    username: root
    password: 123
    driver-class-name: com.mysql.jdbc.Driver
  application:
    name: user-service # 应用名称
eureka:
  client:
    service-url: # EurekaServer地址
      defaultZone: http://127.0.0.1:8080/eureka
  instance:
    prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称(也就是根据主机名还是ip访问)
    ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找

注解

// 两者选其一
@EnableEurekaClient // 开启EurekaClient功能
@EnableDiscoveryClient // 开启Eureka客户端

客户端又分为生产者和消费者,生产者也就是服务提供者,需要将自身注册到服务中心即可,而消费者需要到服务中心调用对应的服务,服务中心存储的相当于服务名称=多个服务地址的键值对

使用DiscoveryClient类

 // 必须导入org.springframework.cloud.client.discovery.DiscoveryClient
 @Autowired
 private DiscoveryClient discoveryClient;

 public void demo(){
        // 根据服务名称,拿到所有已注册的服务实例,因为可能会有集群所以是个集合
        List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
        // 拿到其中一个实例
        ServiceInstance serviceInstance = instances.get(0);
        // host主机地址
        String host = serviceInstance.getHost();
        // port端口号
        int port = serviceInstance.getPort();
        String url = "http://"+host+":"+port+"controller地址及参数";
    }

结合RestTemplate使用

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate();
        // 解决乱码
        restTemplate.getMessageConverters().add(1,new StringHttpMessageConverter(Charset.forName("UTF-8")));
        return restTemplate;
    }
}
#修改Eureka服务实例的显示
eureka:
  instance:
    instance-id: ${spring.application.name}:${server.port}

负载均衡

​ 首先负载均衡是在集群的基础上,集群就是将相同功能的代码部署到不同的服务器,而负载均衡则是将流量平均分发到集群的不同服务上,同时做集群的目的也就是为了负载均衡,其中负载均衡又有很多策略,也就是实现负载均衡的算法

​ Eureka中集成了Ribbon负载均衡器,使用时只需要简单的配置即可

​ 使用

​ 在RestTemplate的配置方法上添加@LoadBalanced注解:

​ 添加该注解后,才可以使用服务名的方式访问

@Configuration
public class RestTemplateConfig {
    @Bean
    // 开启负载均衡
    @LoadBalanced
    public RestTemplate restTemplate(){
        RestTemplate restTemplate = new RestTemplate();
        // 解决乱码
        restTemplate.getMessageConverters().add(1,new StringHttpMessageConverter(Charset.forName("UTF-8")));
        return restTemplate;
    }
}

​ 修改调用方式,从host+port改为直接调用服务名称的方式

http://服务名称/controller方法的映射地址
String url = "http://user-service/user/"+id;

​ 默认使用的是轮询机制,注入RibbonLoadBalanceClient类进行测试

@RunWith(SpringRunner.class)
@SpringBootTest(classes = 启动类.class)
public class LoadBalanceTest {

    @Autowired
    RibbonLoadBalancerClient client;

    @Test
    public void test(){
        for (int i = 0; i < 100; i++) {
            ServiceInstance instance = this.client.choose("集群的服务名");
            System.out.println(instance.getHost() + ":" + instance.getPort());
        }
    }
}

​ 修改负载均衡的策略

​ 查看IRule接口的实现类,可以找到其他实现负载均衡的策略

​ 在客户端添加配置

# 根
user-service: # 服务名
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #策略

高可用Eureka

将多个EurekaServer相互注册为服务

启动多个EureakServe服务,进行相互注册,只需要将注册地址的ip互换

server:
  port: 10086 # 端口
spring:
  application:
    name: eureka-server # 应用名称,会在Eureka中显示
eureka:
  client:
    service-url: # 配置其他Eureka服务的地址,而不是自己,比如10087
      defaultZone: http://127.0.0.1:10087/eureka

客户端注册到Eureka服务集群

eureka:
  client:
    service-url: # EurekaServer地址,多个地址以','隔开 EurekaServer集群只需注册在一台上即可共享
      defaultZone: http://127.0.0.1:10086/eureka

服务续约

eureka:
  instance:
    lease-expiration-duration-in-seconds: 90 #超过90秒默认该服务宕机
    lease-renewal-interval-in-seconds: 30 #每30秒发送一次心跳

当服务消费者启动时,会检测eureka.client.fetch-registry=true参数的值,如果为true,则会从Eureka Server服务的列表只读备份,然后缓存在本地。并且每隔30秒会重新获取并更新数据。我们可以通过下面的参数来修改:

eureka:
  client:
    registry-fetch-interval-seconds: 5

失效剔除和自我保护

失效剔除

有些时候,我们的服务提供方并不一定会正常下线,可能因为内存溢出、网络故障等原因导致服务无法正常工作。Eureka Server需要将这样的服务剔除出服务列表。因此它会开启一个定时任务,每隔60秒对所有失效的服务(超过90秒未响应)进行剔除。

可以通过eureka.server.eviction-interval-timer-in-ms参数对其进行修改,单位是毫秒,生成环境不要修改。

这个会对我们开发带来极大的不便,你对服务重启,隔了60秒Eureka才反应过来。开发阶段可以适当调整,比如10S

自我保护

我们关停一个服务,就会在Eureka面板看到一条警告:

1525618396076

这是触发了Eureka的自我保护机制。当一个服务未按时进行心跳续约时,Eureka会统计最近15分钟心跳失败的服务实例的比例是否超过了85%。在生产环境下,因为网络延迟等原因,心跳失败实例的比例很有可能超标,但是此时就把服务剔除列表并不妥当,因为服务可能没有宕机。Eureka就会把当前实例的注册信息保护起来,不予剔除。生产环境下这很有效,保证了大多数服务依然可用。

但是这给我们的开发带来了麻烦, 因此开发阶段我们都会关闭自我保护模式:

在eureka的yml文件中配置

eureka:
  server:
    enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
    eviction-interval-timer-in-ms: 1000 # 扫描失效服务的间隔时间(缺省为60*1000ms)

重试机制

​ CAP原则:CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得

Eureka的服务治理强调了CAP原则中的AP,即可用性和可靠性。它与Zookeeper这一类强调CP(一致性,可靠性)的服务治理框架最大的区别在于:Eureka为了实现更高的服务可用性,牺牲了一定的一致性,极端情况下它宁愿接收故障实例也不愿丢掉健康实例,正如我们上面所说的自我保护机制。

但是,此时如果我们调用了这些不正常的服务,调用就会失败,从而导致其它服务不能正常工作!这显然不是我们愿意看到的。

spring:
  cloud:
    loadbalancer:
      retry:
        enabled: true # 开启Spring Cloud的重试功能
user-service:
  ribbon:
    ConnectTimeout: 250 # Ribbon的连接超时时间
    ReadTimeout: 1000 # Ribbon的数据读取超时时间
    OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
    MaxAutoRetriesNextServer: 1 # 切换实例的重试次数
    MaxAutoRetries: 1 # 对当前实例的重试次数

根据如上配置,当访问到某个服务超时后,它会再次尝试访问下一个服务实例,如果不行就再换一个实例,如果不行,则返回失败。切换次数取决于MaxAutoRetriesNextServer参数的值

引入spring-retry依赖,测试

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
Copyright © TaoQZ 2019 all right reserved,powered by Gitbook作者联系方式:taoqingzhou@gmail.com 修订时间: 2024-11-19 17:25:43

results matching ""

    No results matching ""

    results matching ""

      No results matching ""