前言

在分布式系统领域有个著名的CAP定理:

C——数据一致性;

A——服务可用性;

P——服务对网络分区故障的容错性。

这三个特性在任何分布式系统中不能同时满足,最多同时满足两个。

Zookeeper是著名Hadoop的一个子项目,很多场景下Zookeeper也作为Service发现服务解决方案。Zookeeper保证的是CP,即任何时刻对Zookeeper的访问请求能得到一致的数据结果,同时系统对网络分割具备容错性,但是它不能保证每次服务请求的可用性。

而Spring Cloud Netflix在设计Eureka时遵守的就是AP原则。因为对于服务消费者来说,能消费才是最重要的——拿到可能不正确的服务实例信息后尝试消费一下,也好过因为无法获取实例信息而不去消费。所以,对于服务发现而言,可用性比数据一致性更加重要——AP胜过CP。

1、服务注册与服务发现

服务治理是微服务架构中最为核心和基础的模块,主要为各个微服务实例提供自动化注册与发现功能。

服务注册:服务治理框架会构建一个注册中心,每个服务向注册中心注册登记自己提供的服务,将服务名、主机、端口号、版本号、通信协议等告知注册中心,注册中心按服务名分类组织成一个服务清单。

例如:

有三个提供订单服务的实例分别运行在 192.168.0.100:8081、192.168.0.101:8082、192.168.0.102:8083;

还有四个提供用户服务的实例分别运行在 192.168.1.100:8091、192.168.1.101:8092、192.168.1.102:8093、192.168.1.103:8094;

当这些服务全部启动并成功注册到注册中心上后,注册中心会维护一份类似下面的一个服务清单:

服务名 服务地址
订单服务 192.168.0.100:8081、192.168.0.101:8082、192.168.0.102:8083
用户服务 192.168.1.100:8091、192.168.1.101:8092、192.168.1.102:8093、192.168.1.103:8094

服务发现:在服务治理框架下,服务之间不再通过具体实例地址来相互调用,而是通过向注册中心咨询并获取服务清单,以实现(通过服务名)对具体服务的调用。

  例如:前台用户下单需要调用订单服务,首先向注册中心发起咨询订单服务的请求,注册中心返回订单服务位置清单,然后调用方通过负载策略选择其中某一个位置进行服务调用即可。

2、单点注册中心

创建一个空项目,pom.xml如下(后面都基于这个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">
<modelVersion>4.0.0</modelVersion> <groupId>com.springcloud.demo</groupId>
<artifactId>springcloud-demo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>eureka-server</module>
<module>eureka-service-provider</module>
</modules> <name>Maven</name>
<!-- FIXME change it to the project's website -->
<url>http://maven.apache.org/</url>
<inceptionYear>2001</inceptionYear> <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.8.RELEASE</version>
<relativePath/>
</parent> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<failIfNoTests>true</failIfNoTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

1、服务注册中心

首先搭建一个注册中心,命名为eureka-server,引入eureka依赖包

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

在 Spring Boot 启动入口文件上加注解 @EnableEurekaServer

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run( EurekaServerApplication.class, args );
}
}

然后配置application.yml:

server:
port: 8081 eureka:
instance:
hostname: eurekaserver
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ spring:
application:
name: eurka-server

其中:

eureka.client.registerWithEureka = false     设置为false表示不向注册中心注册自己,默认为true
eureka.client.fetchRegistry = false               设置为false表示不需要去检索服务,默认为true

启动应用并访问 http://localhost:8081/ ,如下图所示:

可以看到注册中心还没有注册任何服务,至此注册中心已经搭建完毕。

2、服务提供者

搭建好注册中心后,我们再来创建一个服务提供者,然后将其注册到注册中心。新建一个项目eureka-service-provider,添加eureka依赖,在 Spring Boot 启动入口文件上加注解 @EnableEurekaClient

@SpringBootApplication
@EnableEurekaClient
public class EurekaServiceApplication {
public static void main(String[] args) {
SpringApplication.run( EurekaServiceApplication.class, args );
}
}

配置application.yml:

server:
port: 8082 eureka:
instance:
hostname: service-provider
client:
serviceUrl:
defaultZone: http://localhost:8081/eureka/ spring:
application:
name: service-1

因为需要向注册中心注册,所以 eureka.client.registerWithEureka、eureka.client.fetchRegistry 不要配置为 false

启动eureka-service-provider,刷新注册中心,可以看到服务已经注册成功了:

同时eureka-server控制台会打印一行日志:Registered instance SERVICE-1/xxxx:service-1:8082 with status UP (replication=false)

3、高可用注册中心

上面只是一个单点注册中心,微服务场景下应用通过注册中心来访问真实服务,如果注册中心挂了会造成服务无法调用,所以注册中心也得要保持高可用,Eureka 不可能没有想到这一点,其实在Eureka里,所有结点既是服务提供方,又是服务消费方,注册中心也不例外。

大家还记得在单节点配置里讲到的这两个配置吗:

eureka.client.registerWithEureka = false

eureka.client.fetchRegistry = false

实际上 Eureka 高可用方案就是将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组相互注册的服务注册中心,在这组服务注册中心内部相互同步注册清单,形成一个高可用集群

注册中心集群

创建以下三个Eureka Server:peer1、peer2、peer3

peer1的配置如下(peer2、peer3配置类似):

server:
port: 8081 eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: http://peer2:8082/eureka/,http://peer3:8083/eureka/ spring:
application:
name: eurka-server

分别启动peer1、peer2、peer3,访问peer1:8081

单点故障

这时候如果peer2突然挂了,访问peer1和peer3仍然可以看到peer2,这是因为Eureka默认开启了自我保护机制(peer2挂了没有剔除),可以通过设置 eureka.server.enable-self-preservation=false 来禁用保护

peer1的配置文件里新增eureka.server.enable-self-preservation=false设置,然后模拟peer3挂了,peer1注册中心如下:

大概两分钟左右,从peer1注册中心可以看到 registered-replicas 有3个,unavailable-replicas 里有 peer3,available-replicas 里只有 peer1 和 peer2 可用,

而从 peer2 里看 peer3 仍然可用(过比较长的时间,peer2 仍然会将 peer3 从 available-replicas 里剔除)

将serverce-1注册到注册中心peer1,然后在peer1 和 peer2里都可以看到service-1。

故障恢复

peer3 经过运维处理恢复了,重新启动peer3自动加入了peer1和peer2集群,可用看到service-1也自动注册到peer3上了:

4、配置

服务注册相关配置:eureka.instance 为前缀对应的Class

org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean

服务实例相关配置:eureka.client 为前缀对应的Class

org.springframework.cloud.netflix.eureka.EurekaClientConfigBean

服务配置属性以eureka.server为前缀对应的Class

org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean

client常见配置

# 启用Eureka客户端
eureka.client.enabled=true
# 从Eureka服务端获取注册信息的间隔时间(单位:秒)
eureka.client.registry-fetch-interval-seconds=30
# 是否从Eureka服务端获取注册信息
eureka.client.fetch-registry=true
# 是否要将自身的实例信息注册到Eureka服务端
eureka.client.register-with-eureka=true
# 初始化实例信息的变化到Eureka服务端的间隔时间(单位:秒)
eureka.client.instance-info-replication-interval-seconds=40
# 从Eureka客户端到Eureka服务端的连接总数
eureka.client.eureka-server-total-connections=200
# 获取实例时是否过滤,仅保留UP状态的实例
eureka.client.filter-only-up-instances=true

server常见配置

#设为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.85

instance常见配置

#是否优先使用IP地址作为主机名的标识,如果不配置就是机器的主机名
eureka.instance.prefer-ip-address=false
#Eureka客户端向服务的发送心跳的时间间隔(单位:秒)
eureka.instance.lease-renewal-interval-in-seconds=30
#Eureka服务的收到最后一次心跳之后等待的时间上限(单位:秒),
#超过这个时间之后会将该服务实例从注册中心清单里剔除
eureka.instance.lease-expiration-duration-in-seconds=90

附录

  本文Demo地址

  Spring Cloud Netflix官方文档

  Spring Cloud 中文社区

  spring-cloud-book

服务治理

SpringCloudLesson

更多配置参考

最新文章

  1. Nginx服务器之Nginx与tomcat结合访问jsp
  2. 在SharePoint中创建可自定义属性的文件夹
  3. php常见问题
  4. 【poj2960】 S-Nim
  5. 玩转cocos2d-x lua-binding, 实现c++与lua混合编程
  6. windowns--HANDLE,
  7. python编写接口
  8. html5页面中拨打电话的方式
  9. registerForRemoteNotificationTypes: is not supported in iOS 8.0 and
  10. 关于Verilog HDL的一些技巧、易错、易忘点(不定期更新)
  11. Mac上好用的视频播放器有哪些?
  12. 使用axios以及http-proxy-middleware代理处理跨域的问题
  13. App分享之微信微博等各个社交平台的分享授权规则和常见问题
  14. python中print和input的底层实现
  15. C#读写三菱PLC和西门子PLC数据 使用TCP/IP 协议
  16. 第一次使用zxxbox弹层经历
  17. &lt;&lt;attention is all you need&gt;&gt;速读笔记
  18. 谈一谈对MySQL InnoDB的认识及数据库事物处理的隔离级别
  19. HGOI20180814 (NOIP 模拟Day1)
  20. Javascript-逻辑或(||)

热门文章

  1. C++贪心算法实现活动安排问题
  2. govendor用法
  3. Stage3D大冒险
  4. 【转】LockSupport深入浅出
  5. Alpha4
  6. Node.js官方文档:到底什么是阻塞(Blocking)与非阻塞(Non-Blocking)?
  7. 分布式系统原理之cap理论
  8. 逆向破解之160个CrackMe —— 023-024
  9. python关于type()与生成器generator的用法
  10. excel隔行选中内容如何操作