原来我们服务间的调用,一般都是通过在消费者端配置生产者的ip和端口,来进行调用,但是,在微服务的环境中,服务数量可能会很多,而且,服务之间可能调用关系也会很多,如果还是配置ip和端口来互相调用的话,这个配置的工作就很庞杂,同时,如果某个微服务的ip发生了变化,那么带来的维护工作,也会很复杂。
因此,微服务中引入了服务发现组件,也就是注册中心,一般使用Eureka,也有其他的组件,Consul,zookeeper等。实际使用中,将微服务都注册到注册中心,注册中已经包含了微服务的ip等信息,这样,微服务之间互相调用时,就可以现在注册中心获取对应微服务的ip信息。这样就不用自己维护很多的配置文件了。
Eureka是Netflix开源的服务发现组件,本身是一个基于REST的服务,包含Server和Client两部分,Spring Cloud将它集成在子项目Spring Cloud Netflix中。在微服务系统中,我们需要单独创建一个Eureka Server作为注册中心,其他的微服务就相当于客户端,注册到我们的注册中心中。
Finchley.SR2
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
@EnableEurekaServer
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:8761/eureka/
配置解析:
eureka.client.register-with-eureka=false
表示是否将自己注册到Eureka Server,默认值trueeureka.client.fetch-registry=false
表示是否从Eureka Server获取信息,默认值tureeureka.client.serviceUrl.defaultZone
: 表示Eureka Server的地址,如果有多个,使用逗号分隔。Finchley.SR2
版本,需要引入web依赖:<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
@EnableDiscoveryClient
Edgware
版本开始,也可以省略不写server:
port: 8762
spring:
application:
name: auth-server
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
spring.application.name
: 表示注册到Eureka Server中服务名启动两个服务,直接访问Eureka Server地址,结果如图
可以看到,服务注册成功;这样,如果有多个服务之间互相调用,就可以从Eureka Server中获取到对应服务的信息,这里暂时先不演示;
但是,存在一个问题,服务注册时,默认都是通过hostname来注册的,也就是这里的localhost,但是,如果服务不在同一个服务器上,显然是调用不了的,因此,我们再客户端增加一个配置:
另外,如果客户端所在的服务器,有多个网卡的情况下,使用这种方式,怎么知道注册哪个ip呢?
Spring Cloud提供了按需选择ip的能力:
spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*
- VM.*
spring:
cloud:
inetutils:
preferredNetworks:
- 192.168
spring:
cloud:
inetutils:
use-only-site-local-interfaces: true
eureka:
instance:
ip-address: 127.0.0.1
上边的例子中,已经展示了Eureka的基本用法,其中Eureka的控制台,只要输入对应的ip和端口,就可以访问,这肯定不符合企业的安全规范,一般情况下都是需要输入一个用户名和密码;这里展示简单的使用Security来添加注册中心的身份验证:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
security:
basic:
enable: true # 开启基于HTTP basic的认证
user:
name: root
password: root
以上配置完成后,使用浏览器访问Eureka Server,就会提示输入用户名和密码,才能访问;那么,注册中心增加了身份验证之后,微服务要如何才能注册呢?
微服务的注册,只需要将配置Eureka Server的地址添加上用户名和密码就可以了,具体配置为:
eureka:
client:
service-url:
# http://username:password@EUREKA_HOST:EUREKA_PORT/eureka/
defaultZone: http://root:root@localhost:8761/eureka/
由于,我使用的Spring Boot
版本为2.0.7.RELEASE
,Eureka Server配置需要稍作修改:
Spring Boot 2.0中security舍弃了一些配置,因此配置需要修改如下:
spring:
security:
user:
name: root
password: root
然后,需要增加配置类,否则客户端注册会报错,cannot execute any request on any know server:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
如果Eureka Server在一定时间内没有收到微服务实例的心跳,会将该微服务注销,即从注册表中移除;但是,有时候因为网络或者其他的原因没有收到心跳,实例还是正常可用,这种情况下,如果将实例从注册表中移除,访问该微服务本来是应该正常了,却导致了访问失败,造成系统的误报同时难以排查问题;
为了防止上述情况发生,Eureka Server默认开启了自我保护机制,如果短时间内,注册中心丢失了过多的客户端,Eureka Server会进入自我保护状态,该状态下,会保护注册表中的信息,不会从注册表中删除数据,也就是,虽然没有收到微服务实例的心跳,但是,还会保存该实例的注册信息,不会将之移除。
如果不想让Eureka Server使用自我保护机制,我们可以在配置文件中增加如下配置:
eureka:
server:
enable-self-preservation: false
根据前文中图片,微服务实例的状态对应的是UP,表示该服务可用;我们也知道了Eureka Server通过心跳来验证微服务是否正常;但是,有时候微服务正常运行,也不一定可用,可能微服务中的某个组件不可用,或者,数据源连接不上,导致微服务不可用;但是,Eureka Server通过心跳并不能获取这些信息,还是会显示UP状态,因此,我们还需要对客户端进行健康检查。
微服务的健康检查,基于actuator包,因此,在客户端需要引入依赖,
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
(ps: 引入依赖后,启动类微服务,在IDE控制台可以看到,增加了几个请求路径,/actuator/health
,/actuator/info
等,Spring Boot 2.0之前可能是/health,/info;我们可以手动请求该路径,查看微服务的健康状态)
然后,增加配置,启动健康检查,将健康状态传播到Eureka Server
eureka:
client:
healthcheck:
enabled: true
前文描述的Eureka Server都是单机状态,如果,我们想要实现高可用,搭建Eureka Server集群,也非常简单,只需要将Eureka Server作为微服务注册到其他的Server中;其他的微服务注册,原来的地址直接配置多个就可以了,使用逗号分隔,例如:
eureka:
client:
service-url:
defaultZone: http://username:password@localhost:8761/eureka/,http://username:password@localhost:8762/eureka
Eureka Server和普通的微服务实例配置类似,只是url中,不包含自己的,也就是前文所说的注册到其他的Server。