基础知识
- 什么是服务治理
Spring Cloud封装了 Netflix公司开发的 Eureka模块来实现服务治理
在传统的rpc远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。 - 什么是服务注册与发现
Eureka采用了CS的设计架构, Eureka Server作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用,RPC远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址)

Eureka包含两个组件: Eureka ServerEureka和 Client
- Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在 EurekaServer中进行注册,这样 Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。 - EurekaClient通过注册中心进行访问
是一个Java客户端,用于简化 Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin-)负载算法的负载均衡器。在应用启动后,将会向 Eureka Server发送心跳(默认周期为30秒)。如果 Eureka Server在多个心跳周期内没有接收到某个节点的心跳, EurekaServer将会从服务注册表中把这个务节点移除(默认90秒)
EurekaServer服务端安装
引言
Eureka 又称 服务注册中心,全部服务都需要进行注册才能使用,也是微服务架构中必不可少的一个组件。
Eureka是服务注册中心,不是服务调用
Eureka 服务端
创建module
创建一个叫cloud-eureka-server7001的module
改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"><parent><artifactId>cloud2020</artifactId><groupId>com.sgy.cloud2020</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>cloud-eureka-server7001</artifactId><dependencies><!-- eureka-server --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency><!-- 引用自己定义的api通用包,可以使用Payment支付Entity --><dependency><groupId>com.sgy.cloud2020</groupId><artifactId>cloud-api-commons</artifactId><version>${project.version}</version></dependency><!--监控--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies></project>
Cannot resolve org.springframework.cloud:spring-cloud-starter-netflix-eureka-server:unknown
首先 看一下错误:

原因很简单,是因为没有指定依赖的版本号,但是我明明在父依赖中引入了spring-cloud-alibaba-dependencies,他会管理所有的组件版本号,那为什么没有管理改组件的版本号呢
这是因为我将<scope>import</scope>写成了<scope>runtime</scope>
应该改成如下
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.1.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
启动类
package com.sgy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* Created by AaronShen on 2020/5/27
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7001.class,args);
}
}
改yml
server:
port: 7001
eureka:
instance:
hostname: localhost #eureka服务端的实例名称
client:
# false表示不向注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
fetch-registry: false
service-url:
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
# 单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 相互注册
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
server:
#关闭自我保护模式,保证不可用服务被及时删除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
测试

现在还没有服务注册
EurekaClient端cloud-provider-paymen8090将注册进EurekaServe成为服务提供者
改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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.sgy.cloud2020</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-provider-payment8090</artifactId>
<dependencies>
<dependency>
<groupId>com.sgy.cloud2020</groupId>
<artifactId>cloud-provider</artifactId>
<version>${project.version}</version>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
修改主启动类
package com.sgy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* Created by AaronShen on 2020/5/26
*/
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8090 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8090.class,args);
}
}
改yml
一定要写spring.application.name,因为服务注册是根据name进行注册的
server:
port: 8090
spring:
application:
name: cloud-provider-payment
datasource:
username: blog
password: 123456
url: jdbc:mysql://192.168.200.10:3306/cloud?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
# 使用我们自己的druid数据源
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 10 #初始化连接个数
minIdle: 5 #最小连接个数
maxActive: 500 #最大连接个数
maxWait: 60000 #最大等待时间
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
#defaultZone: http://eureka7001.com:7001/eureka/
mybatis:
# config-location和configuration不能同时配置,否则会抛出异常
# 一般只配置configuration即可,会自动找到mybatis全局配置文件
# config-location: classpath:mybatis/mybatis-config.xml
mapper-locations: classpath:mapper/*Mapper.xml
# 对应实体类的路径,只能指定具体的包,多个配置可以使用英文逗号隔开
type-aliases-package: com.sgy.payment
configuration:
# Mybatis SQL语句控制台打印
# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 开启驼峰命名规则
# 在数据库中字段可以采用驼峰命名规则,mybatis会把 下划线去掉并把下划线后面的首字母认为是大写
map-underscore-to-camel-case: true
logging:
level:
# 注意注意注意 一定要修改成自己的包名
com.sgy: debug
file:
path: log/
name: log/com.sgy.payment-dev.log
clean-history-on-start: true
pattern:
console: "%d{yyyy-MM-dd} [%thread] %-5level %logger{50} ===> %msg%n"
file: "%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ===> %msg%n"
测试
浏览器访问:http://127.0.0.1:7001

EurekaClient端cloud-consumer-order80将注册进EurekaServe成为服务消费者)
改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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.sgy.cloud2020</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-consumer-order80</artifactId>
<dependencies>
<dependency>
<groupId>com.sgy.cloud2020</groupId>
<artifactId>cloud-consumer</artifactId>
<version>${project.version}</version>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
改启动类
package com.sgy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* Created by AaronShen on 2020/5/26
*/
@SpringBootApplication
@EnableEurekaClient
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
修改yml
server:
port: 80
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
#defaultZone: http://eureka7001.com:7001/eureka/
spring:
application:
name: cloud-order-service
测试
浏览器访问:http://127.0.0.1:7001

Eureka集群
集群原理说明
集群的搭建:互相注册,互相守望

问题:微服务RPC远程服务调用最核心的是什么
高可用,试想你的注册中心只有一个 only one,它出故障了那就呵呵()”了,会导致整个为服务环境不可用,所以
搭建Eureka集群
再创建一个cloud-eureka-server7002
改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">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.sgy.cloud2020</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-server7002</artifactId>
<dependencies>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- 引用自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.sgy.cloud2020</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
改主启动类
package com.sgy.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* Created by AaronShen on 2020/5/27
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer7002 {
public static void main(String[] args) {
SpringApplication.run(EurekaServer7002.class,args);
}
}
改本地域名解析映射
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
在命令行窗口输入ipconfig /flushdns执行,刷新本地的DNS缓存数据。
改yml [cloud-eureka-server7002]
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端的实例名称
client:
# false表示不向注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
fetch-registry: false
service-url:
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
# 单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 相互注册
defaultZone: http://eureka7001.com:7001/eureka/
server:
#关闭自我保护模式,保证不可用服务被及时删除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
改yml [cloud-eureka-server7001]
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
# false表示不向注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
fetch-registry: false
service-url:
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
# 单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 相互注册
defaultZone: http://eureka7002.com:7002/eureka/
server:
#关闭自我保护模式,保证不可用服务被及时删除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
测试


订单支付两服务注册进Eureka集群
改支付服务pom
server:
port: 8090
spring:
application:
name: cloud-payment-server
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka,http://eureka7001.com:7001/eureka/ #集群版
#defaultZone: http://eureka7001.com:7001/eureka/
改订单服务pom
server:
port: 80
spring:
application:
name: cloud-consumer-server
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka,http://eureka7001.com:7001/eureka/ #集群版
#defaultZone: http://eureka7001.com:7001/eureka/
测试
- 先启动eureka7001和7002服务注册中心集群
- 启动订单服务和支付服务
- 浏览器访问 http://eureka7001.com:7001,http://eureka7002.com:7002,


Eureka自我保护
理论知识
保护模式主要用于一组客户端和 Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,
Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
如果在 Eureka Server的首页看到以下这段提示,则说明 Eureka进入了保护模式:
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY’RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE
一句话解释
一句话:某时刻某一个微服务不可用了, Eureka不会立刻清理,依旧会对该微服务的信息进行保存
为什么会产生 Eureka自我保护机制?
为了防止 EurekaClient可以正常运行,但是与 EurekaServer网络不通情况下, EurekaServer不会立刻将 Client服务剔除
什么是自我保护模式?
默认情况下,如果 EurerEurekaServer在一定时间内没有接收到某个微服务实例的心跳,将会注销该实例(默认90秒)但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与 EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。 Eureka通过“自我保护模式来解决这个问题当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。
自我保护机制:默认情况下 EurekaClientE定时向端发送心跳包,如果 Eurekaserver在端在一定时间内(默认90秒没有收到EurekaClient发送心跳包,便会直接从服务注册列表中剔除该服务,但是在短时间(90秒中)内丢失了大量的服务实例心跳,这时候 EurekaServer会开启自我保护机制,不会剔除该服务(该现象可能出现在如果网络不通但是 EurekaClient为出现宕机,此时如果换做别的注册中心如果一定时间内没有收到心跳会将剔除该服务,这样就出现了严重失误,因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的)
在自我保护模式中, Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让 Eureka集群更加的健壮、稳定。
如何禁止自我保护
EurekaServer7001配置文件
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
# false表示不向注册中心注册自己
register-with-eureka: false
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务
fetch-registry: false
service-url:
# 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
# 单机 defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
# 相互注册
# 集群版本
# defaultZone: http://eureka7002.com:7002/eureka/
# 单机版本
defaultZone: http://eureka7001.com:7001/eureka/
server:
#关闭自我保护模式,保证不可用服务被及时删除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000
支付提供者8090的配置文件
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka7002.com:7002/eureka,http://eureka7001.com:7001/eureka/ #集群版
#defaultZone: http://eureka7001.com:7001/eureka/
instance:
instance-id: payment8090
prefer-ip-address: true #访问路径可以显示ip地址
# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
lease-expiration-duration-in-seconds: 2
spring cloud eureka 参数配置
配置项说明
eureka.client.registry-fetch-interval-seconds
表示eureka client间隔多久去拉取服务注册信息,默认为30秒,对于api-gateway,如果要迅速获取服务注册状态,可以缩小该值,比如5秒
eureka.instance.lease-expiration-duration-in-seconds
leaseExpirationDurationInSeconds,表示eureka server至上一次收到client的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该instance。
- 默认为90秒
- 如果该值太大,则很可能将流量转发过去的时候,该instance已经不存活了。
- 如果该值设置太小了,则instance则很可能因为临时的网络抖动而被摘除掉。
- 该值至少应该大于leaseRenewalIntervalInSeconds
eureka.instance.lease-renewal-interval-in-seconds
leaseRenewalIntervalInSeconds,表示eureka client发送心跳给server端的频率。如果在leaseExpirationDurationInSeconds后,server端没有收到client的心跳,则将摘除该instance。除此之外,如果该instance实现了HealthCheckCallback,并决定让自己unavailable的话,则该instance也不会接收到流量。
- 默认30秒
eureka.server.enable-self-preservation
是否开启自我保护模式,默认为true。
默认情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。
Eureka通过“自我保护模式”来解决这个问题——当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
eureka.server.eviction-interval-timer-in-ms
eureka server清理无效节点的时间间隔,默认60000毫秒,即60秒
测试环境参考配置
eureka server
eureka:
server:
enable-self-preservation: false # 关闭自我保护模式(缺省为打开)
eviction-interval-timer-in-ms: 5000 # 续期时间,即扫描失效服务的间隔时间(缺省为60*1000ms)
eureka client
eureka:
instance:
lease-renewal-interval-in-seconds: 5 # 心跳时间,即服务续约间隔时间(缺省为30s)
lease-expiration-duration-in-seconds: 10 # 发呆时间,即服务续约到期时间(缺省为90s)
client:
healthcheck:
enabled: true # 开启健康检查(依赖spring-boot-starter-actuator)
zuul
eureka:
client:
registry-fetch-interval-seconds: 5 # 默认为30秒
