springcloud组件-eureka
1、eureka什么?
2、eureka单节点如何使用?
3、eureka集群配置?
4、zookeeper的cp性与eureka的ap性
5、关于eureka的自我保护机制
1、eureka是什么?
springcloud中的eureka就相当于之前dubbo中的zookeeper,也是一个注册中心。
一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,并且支持集群部署。
2、eureka单节点?
以自身所写的项目来说:
首先新建一个父项目Springcloud,分别去新建子项目:
springcloud-consumer-management:各个消费者项目
springcloud-provider-management:各个提供者项目
springcloud-management:MVC层
springcloud-eureka-management:eureka项目
下一步所要做的是:
(1)在父项目中引入所需要的所有jar包
(2)在springcloud-management中将Mapper、model、service层新建出来,
并进行层级依赖的建立:mapper-->model service-->mapper
(3)在consumer-management中去引入对应的依赖,并新建一个consumer的子工程:6081
(4)在provider-management中去引入对应的依赖,并新建一个consumer的子工程:8081
实现:
(1)父项目的依赖:
1 <!-- 2 项目jar包的构建: 3 之前使用的parent标签直接引入项目中,可以继承父项目的jar包 4 但是项目中已经使用到了springcloud和springboot 5 springcloud和springboot需要些两个parent,但是在pom工程中不能这样实现 6 所以直接引入springboot和springcloud的jar包工程,并且使用dependencyManagement标签进行管理,用到了什么jar包直接重写 7 8 springboot-parent, springcloud-parent的父级工程都是dependencies 9 --> 10 11 <dependencyManagement> 12 <dependencies> 13 <dependency> 14 <groupId>org.springframework.boot</groupId> 15 <artifactId>spring-boot-dependencies</artifactId> 16 <version>2.1.8.RELEASE</version> 17 <type>pom</type> 18 <scope>import</scope> 19 </dependency> 20 <dependency> 21 <groupId>org.springframework.cloud</groupId> 22 <artifactId>spring-cloud-dependencies</artifactId> 23 <version>Greenwich.SR3</version>注意版本号 24 <type>pom</type> 25 <scope>import</scope> 26 </dependency> 27 <dependency> 28 <groupId>io.zipkin.java</groupId> 29 <artifactId>zipkin-server</artifactId> 30 <version>2.11.8</version> 31 <exclusions> 32 <exclusion> 33 <groupId>org.apache.logging.log4j</groupId> 34 <artifactId>log4j-slf4j-impl</artifactId> 35 </exclusion> 36 </exclusions> 37 </dependency> 38 <dependency> 39 <groupId>io.zipkin.java</groupId> 40 <artifactId>zipkin-autoconfigure-ui</artifactId> 41 <version>2.11.8</version> 42 </dependency> 43 <dependency> 44 <groupId>mysql</groupId> 45 <artifactId>mysql-connector-java</artifactId> 46 <version>5.0.4</version> 47 </dependency> 48 <dependency> 49 <groupId>com.alibaba</groupId> 50 <artifactId>druid</artifactId> 51 <version>1.0.31</version> 52 </dependency> 53 <dependency> 54 <groupId>org.mybatis.spring.boot</groupId> 55 <artifactId>mybatis-spring-boot-starter</artifactId> 56 <version>1.3.0</version> 57 </dependency> 58 </dependencies> 59 </dependencyManagement>
(2)springcloud-management中的各层级:
Mapper层的pom:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>springcloud-management</artifactId> 7 <groupId>com.aaa.liu.springcloud</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>springcloud-mapper</artifactId> 13 14 15 <dependencies> 16 <dependency> 17 <groupId>com.aaa.liu.springcloud</groupId> 18 <artifactId>springcloud-model</artifactId> 19 <version>1.0-SNAPSHOT</version> 20 </dependency> 21 <!-- 22 需要引入mysql mybatis springboot druid 23 --> 24 <dependency> 25 <groupId>mysql</groupId> 26 <artifactId>mysql-connector-java</artifactId> 27 </dependency> 28 <dependency> 29 <groupId>com.alibaba</groupId> 30 <artifactId>druid</artifactId> 31 </dependency> 32 <dependency> 33 <groupId>org.mybatis.spring.boot</groupId> 34 <artifactId>mybatis-spring-boot-starter</artifactId> 35 </dependency> 36 <dependency> 37 <groupId>org.springframework.boot</groupId> 38 <artifactId>spring-boot-starter-web</artifactId> 39 </dependency> 40 </dependencies> 41 </project>
service层的pom:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <parent> 6 <artifactId>springcloud-management</artifactId> 7 <groupId>com.aaa.liu.springcloud</groupId> 8 <version>1.0-SNAPSHOT</version> 9 </parent> 10 <modelVersion>4.0.0</modelVersion> 11 12 <artifactId>springcloud-service</artifactId> 13 14 <dependencies> 15 <dependency> 16 <groupId>com.aaa.liu.springcloud</groupId> 17 <artifactId>springcloud-mapper</artifactId> 18 <version>1.0-SNAPSHOT</version> 19 </dependency> 20 </dependencies> 21 </project>
(3)consumer-management依赖:
1 <dependencies> 2 <dependency> 3 <groupId>com.aaa.liu.springcloud</groupId> 4 <artifactId>springcloud-model</artifactId> 5 <version>1.0-SNAPSHOT</version> 6 </dependency> 7 <dependency> 8 <groupId>org.springframework.boot</groupId> 9 <artifactId>spring-boot-starter-web</artifactId> 10 </dependency> 11 </dependencies>
(4)provider-management项目的依赖:
1 <dependencies> 2 <dependency> 3 <groupId>org.springframework.cloud</groupId> 4 <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> 5 </dependency> 6 <dependency> 7 <groupId>org.springframework.boot</groupId> 8 <artifactId>spring-boot-starter-actuator</artifactId> 9 </dependency> 10 <dependency> 11 <groupId>com.aaa.liu.springcloud</groupId> 12 <artifactId>springcloud-service</artifactId> 13 <version>1.0-SNAPSHOT</version> 14 </dependency> 15 </dependencies>
正式开始:
先进行尝试consumer是否能够直接调用到provider中的方法
在provider-management新建的第一个provider为provider-8081,
在Java下的包中只需要添加controller包和一个主启动类:
主启动类:
@SpringBootApplication @MapperScan("com.aaa.liu.springcloud.mapper") public class ApplicationRun8081 { public static void main(String[] args) { SpringApplication.run(ApplicationRun8081.class,args); } }
controller层下的UserController:
@RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/userAll") public List<User> selectAllUsers(){ System.out.println("8081"); return userService.selectAllUsers(); } }
然后在provider下的resources中新建config包,包下是application.properties:
基本的一些配置: server.port=8081 server.servlet.context-path=/ spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false spring.datasource.username=root spring.datasource.password=123456 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource mybatis.type-aliases-package=com.aaa.liu.springcloud.model mybatis.mapper-locations=classpath:mapper/*Mapper.xml
下面是consumer-management中新建一个consumer6081:
主启动类:
@SpringBootApplication public class ApplicationRun6081 { public static void main(String[] args) { SpringApplication.run(ApplicationRun6081.class,args); } }
在Java包下新建了一个config包和controller包
config包下的ConfigRest:
/** * 对配置类,需要加上configuration或者SpringbootApplication * RestTemplate:模拟http协议,使得两个controller调用 */ @SpringBootApplication public class ConfigRest { @Bean public RestTemplate getTemplate(){ return new RestTemplate(); } }
controller包下的UserController:
@RestController public class UserController { @Autowired private RestTemplate restTemplate; //调用service,consumer项目的controller调用的是provider项目的controller(使用的是http协议) //如果可以模拟http协议,就可以实现两个controller之间的调用 //getForObject:(使用get请求方式)该方法有两个参数 //第一个参数:URL(请求路径(localhost:8081/userAll)) //第二个参数:规定返回值的类型 @RequestMapping("/userAll") public List<User> selectAllUsers(){ return restTemplate.getForObject("http://localhost:8081/userAll",List.class); } }
resource下的application.properties(由于只是请求服务,不用与数据打交道,不用数据源):
server.port=6081
server.servlet.context-path=/
完事!分别启动provider和consumer,在浏览器中访问localhost:6081/userAll,请求到数据,则调用无碍。
下面开始eureka的单节点设置:
代码很简单,在eureka-management中新建一个eureka7081项目
在Java中只有一个主启动类:
/** * @EnableEurekaServer:就是开启eureka的服务器 * @EnableEurekaClient:不让用,因为springcloud1.x的注解(我们使用的是格林威治RS3版本) */ @SpringBootApplication @EnableEurekaServer public class ApplicationRun7081 { public static void main(String[] args) { SpringApplication.run(ApplicationRun7081.class,args); } }
resource中的properties.properties:
server.port=7081 server.servlet.context-path=/ # eureka服务器端的实例名称 eureka.instance.hostname=eureka01 # eureka也会把自己注册进注册中心里,这里因为最终eureka是不需要被发现的服务, # 所以不再需要把自己注册到注册中心里 # 默认值为true,表示把自己注册进注册中心 eureka.client.register-with-eureka=false # 表示自己就是注册中心,注册中心的职责是维护服务实例,并不需要从注册中心里去发现自己 eureka.client.fetch-registry=false # 配置eureka的注册地址 # zookeeper需要在linux服务器上进行配置,最终的情况下使用zookeeper的时候必须要指向zookeeper的IP地址 # eureka不需要做任何服务器配置,但是也必须要指向地址(直接指向eureka的项目即可:http://localhost:7081/eureka) # 该地址中的localhost就是eureka.instance.hostname的值,端口号就是server.port的值,/eureka:固定的(eureka自己提供) # defaultZone:eureka默认所提供的命名空间 # 在eureka的注册地址中,是可以分空间(zone)的,是命名空间,根据空间的不同来配置不同集群的eureka来实现不同的作用 # 如果为单节点可以随意配置,但是如果为集群则目前为止必须要配置成defaultZone(因为如果是集群版就必须先要声明命名空间,如果不声明则无法使用) eureka.client.service-url.defaultZone=http://localhost:7081/eureka
在provider8081中进行配置:
现在主启动类中添加:@EnableDiscoveryClient 注解,与eureka的主启动类的@EnableEurekaServer对应
(对于eureka来说,provider就是客户端,而eureka是服务端)
然后就是.properties配置文件了:
spring.datasource.password=123456 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource mybatis.type-aliases-package=com.aaa.liu.springcloud.model mybatis.mapper-locations=classpath:mapper/*Mapper.xml # 配置的是在eureka中显示的服务名(Application) # 如果不配置,则会显示UNKNOWN spring.application.name=user-provider # 配置eureka的注册地址(实现provider这个客户端注册进了eureka中) # GreenWich rs.3:无论是单节点还是集群模式, # eureka的服务器端和客户端的命名空间务必要保持一致,否则会报错(找不到服务实例) eureka.client.service-url.defaultZone=http://localhost:7081/eureka # 是在eureka中配置实例的名字(也就是说Eureka的Status下的显示的名字) # 不允许重复 (相当于mysql数据库中的表的主键id) eureka.instance.instance-id=user-provider-8081 # 在eureka中显示IP地址 eureka.instance.prefer-ip-address=true # 点击eureka的服务名显示出该服务的具体信息 # 根据服务的具体内容,作者,公司,以及对整个服务的描述来进行 # 相当于在HTML页面上有一个a标签<a href="/actuator/info"></a>--> 跳转到controller:@RequestMapping("/actuator/info"> # 开头用info 其他的随便输入 info.author.name=Liu info.company.name=AAA SOFTWARE EDU info.project.description=this is Demo
!这个颜色标注的配置信息是可以不用配置的!
OK!接下类分别启动eureka、provider、consumer(顺序不要错了,否则会报出无法发现服务异常)
在浏览器中访问localhost:7081就可以看到eureka的界面了,在上面可以看到关于注册进eureka的provider信息
3、集群版eureka:
在eureka-management中再新建两个eureka项目,分别为7082、7083
将eureka组成一个集群,eureka与zookeeper不同,eureka组成集群,是需要在eureka配置信息里分别
注入其他两台的信息,让他们能够互相知道。
server.port=7081 server.servlet.context-path=/ # eureka服务器端的实例名称 eureka.instance.hostname=eureka01-->三台分别为eureka01、eureka02、eureka03 # eureka也会把自己注册进注册中心里,这里因为最终eureka是不需要被发现的服务, # 所以不再需要把自己注册到注册中心里 # 默认值为true,表示把自己注册进注册中心 eureka.client.register-with-eureka=false # 表示自己就是注册中心,注册中心的职责是维护服务实例,并不需要从注册中心里去发现自己 eureka.client.fetch-registry=false # 配置eureka的注册地址 # zookeeper需要在linux服务器上进行配置,最终的情况下使用zookeeper的时候必须要指向zookeeper的IP地址 # eureka不需要做任何服务器配置,但是也必须要指向地址(直接指向eureka的项目即可:http://localhost:7081/eureka) # 该地址中的localhost就是eureka.instance.hostname的值,端口号就是server.port的值,/eureka:固定的(eureka自己提供) # defaultZone:eureka默认所提供的命名空间 # 在eureka的注册地址中,是可以分空间(zone)的,是命名空间,根据空间的不同来配置不同集群的eureka来实现不同的作用 # 如果为单节点可以随意配置,但是如果为集群则目前为止必须要配置成defaultZone(因为如果是集群版就必须先要声明命名空间,如果不声明则无法使用) #eureka.client.service-url.defaultZone=http://localhost:7081/eureka
eureka.client.service-url.defaultZone=http://eureka02:7082/eureka,http://eureka03:7083/eureka
与单机版的.properties配置改动很少,主要在两个地方,一个是名字上的区分,一个是最后一行命名空间的配置。
4、zookeeper和eureka的区别?
zookeeper保证的是数据CP性,即数据一致性,而eureka保证的是AP性,即服务可用性。
5、关于eureka的自我保护机制
如果长时间不去连接(激活)eureka的时候,会出现自我保护机制,在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发送心跳,当网络故障/网络延迟/服务宕机等情况下,服务器不再向eureka
发送心跳,eureka从接不到心跳开始计时,默认在90秒(这个时间可以修改)之后,eureka会将这个服务剔除。
但此处有关剔除的机制有一个点:假设现在有100台服务,其中三台别检测认为宕机,eureka会剔除这三台;
但如果此刻有198台provider都没有心跳,eureka不会把这198台provider剔除,而是会保留,这也是eureka的保护机制,
也就是说eureka不会大量剔除服务。这就是eureka的AP性。
为什么eureka不会剔除大量的服务呢?
AP:只保证了服务的可用性,不保证数据的一致性。
CP:只保证数据的一致性,不保证服务的可用性。
如果eureka把所有的服务全部剔除,当consumer尽心访问的时候,发现eureka没有任何服务了,整个项目都处于瘫痪状态,
整个客户端的体验就会很差,也就是相当于500.
假设当大面积服务都没有心跳,eureka不剔除的情况下,consumer进行访问调用的时候依然可以找到服务,然后就可以获取数据,
这些数据可能不是最新的数据,但最起码eureka中还存在服务。
我们是可以将eureka的保护机制关掉的,但通常不允许这么做,一般可以对它进行失效操作。
关闭eureka的自我保护机制:
在eureka-7081项目的application.properties配置文件中,添加:
eureka.server.enable-self-preservation=false
然后重新请求eureka,可以在页面上看到THE SELF PRESERVATION MODE IS TURNED OFF等信息。
让eureka的自我保护机制失效:
首先eureka会定时检测provider的心跳,假如现在eureka检测provider的时间间隔 ,我们给更改为10s.
即在eureka-7081的配置文件中添加:eureka.server.eviction-interval-timer-in-ms=10000
注:单位是毫秒
其次,假如在provider端是一个集群,现在其中一台provider不需要eureka的自我保护机制,也就是说我们要让
eureka的自我保护机制在这一台上失效。
在provider的配置文件中进行配置:告诉eureka,我每隔5秒向你发送心跳,你每隔8秒检测一次,如果没有检测到,
就直接把我剔除。这样一来,eureka会按照我们在provide的配置来执行,我们就打成了让eureka的自我保护失效的目的。
provide配置:
# 规定自己向eureka发送心跳的时间,单位是秒,我们设置为5秒
eureka.instance.lease-renewal-interval-in-seconds=5
# 当eureka最后一次检测到心跳的时间间隔(单位是秒),我们给他设置成8秒
eureka.instance.lease-expiration-duration-in-seconds=8
我们现在对provider进行集群搭建:
在provider-management中新建两个provider8082、provider8083
由于只是对provider进行集群配置,所以三台provider上的项目应该是一样的,只是相较于单台provider来说,
我们需要在配置文件中进行一点修改:
server.port=8081 server.servlet.context-path=/ spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/teach?useSSL=false spring.datasource.username=root spring.datasource.password=root spring.datasource.type=com.alibaba.druid.pool.DruidDataSource mybatis.type-aliases-package=com.aaa.lee.springcloud.model mybatis.mapper-locations=classpath:mapper/*Mapper.xml # 配置的是在eureka中显示的服务名(Application) # 如果不配置,则会显示UNKNOWN(这个是细节!!,以后有问题) spring.application.name=user-provider # 配置eureka的注册地址(实现provider这个客户端注册进了eureka中) # GreenWich RS.3:无论是单节点还是集群模式,eureka的服务器端和客户端的命名空间必要保持一致,否则会报错(找不到服务实例) #eureka.client.service-url.defaultZone=http://localhost:7081/eureka eureka.client.service-url.defaultZone=http://eureka01:7081/eureka,http://eureka02:7082/eureka,http://eureka03:7083/eureka # 是在eureka中配置实例的名字(也就是说Eureka的Status下的显示的名字) # !!!不允许重复!!!(相当于mysql数据库中的表的主键id) eureka.instance.instance-id=user-provider-8081 # 在eureka中显示IP地址 eureka.instance.prefer-ip-address=true # 点击eureka的服务名显示出该服务的具体信息 # 根据服务的具体内容,作者,公司,以及对整个服务的描述来进行显示 # 相当于在HTML页面上有一个a标签<a href="/actuator/info"></a> --> 跳转到controller:@RequestMapping("/actuator/info") # !!开头用info!!其他的随便输入 info.author.name=Liu info.company.name=AAA SOFTWARE EDU info.project.description=This is Demo info.dsadasda=eqwewq321312daa # 规定自己向eureka发送心跳的时间 # 单位是秒 #eureka.instance.lease-renewal-interval-in-seconds=5 # 当eureka最后一次检测到心跳的时间间隔(单位是秒) # eg:15:05:20是最后一次检测到心跳-->检测8秒之后还是无法检测心跳的时候直接剔除 #eureka.instance.lease-expiration-duration-in-seconds=8
这里要注意一点:eureka的同步问题!!!
在provider的配置中,我们需要将provider注册进eureka中, 由于eureka是集群,所以我们在添加配置的时候需要这样写:
eureka.client.service-url.defaultZone=http://eureka01:7081/eureka,http://eureka02:7082/eureka,http://eureka03:7083/eureka
但其实我们只写http://eureka01:7081,我们会发现,在eureka中其它两台eureka中也能看到这台provider,原因是eureka进行了同步。
已有的Eureka Server:在运行过程中,Eureka Server之间会定时同步实例的注册信息。 这样即使新的Application Service只向集群中一台注册服务,则经过一段时间会集群中所有的 Eureka Server都会有这个实例的信息。那么Eureka Server节点之间如何相互发现, 各个节点之间定时(时间由eureka.server.peer-eureka-nodes-update-interval-ms决定)更新节点信息,进行相互发现。 Service Consumer:Service Consumer刚启动时,它会从配置文件读取Eureka Server的地址信息。 当集群中新增一个Eureka Server中时,那么Service Provider如何发现这个Eureka Server?Service Consumer会定时(
此值由eureka.client.eureka-service-url-poll-interval-seconds决定)调用Eureka Server集群接口,获取所有的Eureka Server信息的并更新本地配置。