服务发现

服务化的普及,令软件系统得以通过分布于网络中不同机器的互相协作来复用功能

最早的服务发现可以直接依赖DNS将一个全限定名翻译为一至多个IP地址或者SRV等其他类型的记录便可

但进入微服务时代后,服务宕机 上线下线变得更加频繁 DNS就力不从心了。

服务注册与发现的实现是随zk-eureka-nacos/consul这条线过来的

服务发现原理

批注 2020-07-03 083637

自理式服务发现

202259211731

代理式服务发现

202259211826

服务发现共性设计

在真实系统中,服务发现中心是整个系统的基础架构 如果它一挂 整个系统就完全崩溃了 所以必须进行高可用支持

20201119145641

服务发现中心有以Eureka的AP注册中心 也有以Consul为代表的CP注册中心

当然也有AP CP随时转换的Nacos

AP在出现在系统出现网络分区也能继续对外提供服务 不会影响系统操作的正确性场景下 是十分有用的

服务注册中心的实现

AP实现

2022614162525

Eureka

Eureka是Netflix开发的服务发现框架,Eureka包含两个组件: Eureka Server和Eureka Client.

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

在应用启动后,将会 向Eureka Server发送心跳,默认周期为30秒

保证AP,eureka在设计时优先保证可用性,每一个节点都是平等的,一部分节点挂掉不会影响到正常节点的工作,不会出现类似zk的选举leader的过程

202034165552

批注 2020-07-03 085520 批注 2020-07-03 085612

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
spring.application.name=eureka-server
server.port=8001
#是否将自己注册到注册中心
eureka.client.register-with-eureka=false
#是否从注册中心获取注册信息
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

服务注册

服务续约

eureka:
  instance:
    lease-expiration-duration-in-seconds: 10 # 10秒即过期
    lease-renewal-interval-in-seconds: 5 # 5秒一次心跳

批注 2020-07-03 091553

失效剔除与自我保护

有些时候,我们的服务实例并不一定会正常下线,可能由于内存溢出、网络故障等原因使得服务不能正常工作,而服务注册中心并未收到“服务下线”的请求。为了从服务表中将这些无法提供服务的实例剔除,Eureka Server 在启动的时候会创建一个定时任多默认每隔一一段时间(默认为60秒)将当前清单中超时(默认为90秒)没有续约的服务除出去

默认情况下,EurekaClient会定时向EurekaServer端发送心跳,如果EurekaServer在一定时间内没有收到EurekaClient发送的心跳,便会把该实例从注册服务列表中剔除(默认是90秒),为了防止只是EurekaClient与EurekaServer之间的网络故障,在短时间内丢失大量的实例心跳,这时候EurekaServer会开启自我保护机制,EurekaServer不会踢出这些服务

在开发中,由于会重复重启服务实例,所以经常会出现以下警告:

EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.

所以开发时需要关闭自我保护

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

服务下线

Eureka集群

Eureka 满足AP 牺牲了 C

# eureka1
spring.application.name=spring-cloud-eureka
server.port=8001
eureka.client.serviceUrl.defaultZone=http://localhost:8002/eureka/
# eureka2
spring.application.name=spring-cloud-eureka
server.port=8002
eureka.client.serviceUrl.defaultZone=http://localhost:8001/eureka/
eureka.client.service-url.defaultZone=http://localhost:8001/eureka,http://localhost:8002/eureka

集群同步

Consul

https://www.consul.io/downloads.html
consul agent -dev

生产者配置

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
spring.application.name=consul-producer
server.port=8503
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
#注册到consul的服务名称
spring.cloud.consul.discovery.serviceName=producer

消费者

spring.application.name=consul-consumer
server.port=8504
spring.cloud.consul.host=127.0.0.1
spring.cloud.consul.port=8500
#设置不需要注册到 consul 中
spring.cloud.consul.discovery.register=false
@RestController
public class ServiceController {

    @Autowired
    LoadBalancerClient loadBalancerClient;

    @Autowired
    DiscoveryClient discoveryClient;

    // 获取相关服务实例
    @RequestMapping("/services")
    public Object services(){
        return discoveryClient.getInstances("producer");
    }

    // 自动选择服务实例
    @RequestMapping("/discover")
    public Object discover(){
        return loadBalancerClient.choose("producer").getUri().toString();
    }

    @RequestMapping("/hi")
    public String hi(){
        ServiceInstance instance = loadBalancerClient.choose("producer");

        return new RestTemplate().getForObject(instance.getUri().toString()+"/hi",String.class);
    }
}

zookeeper

保证CP,即任何时刻对zookeeper的访问请求能得到一致性的数据结果,同时系统对网络分割具备容错性,但是它不能保证每次服务的可用性

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
</dependency>
server.port=8101
spring.application.name=zk-producer
spring.cloud.zookeeper.connect-string=127.0.0.1:2181
@EnableDiscoveryClient

Nacos

概念

屏幕截图 2020-09-23 163728

架构

屏幕截图 2020-09-23 163102

注册中心

使用
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
spring.application.name=provider
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
@FeignClient("provider")
public interface ProviderClient {
    @GetMapping("/name")
    String name();
}

@RestController
public static class Api {

    @Autowired
    private ProviderClient client;
    @GetMapping("/")
    public String home() {
        return client.name();
    }
}
vs Zookeeper & Eureka

不同点:

最主要的是Eureka集群中的各个节点是对等的,而Nacos则有主从之分