spring boot actuator提供了很多监控端点,从而了解应用运行情况。
可使用 http://ip:port/endpoint 的形式访问这些端点:
修改服务提供者项目:
org.springframework.boot
spring-boot-starter-actuator
logging: # 配置日志级别,让hibernate打印出执行的SQL
level:
root: INFO
org.hibernate: INFO
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
# INFO
info:
app:
name: @project.artifactId@
encoding: @project.build.sourceEncoding@
java:
source: @java.version@
target: @java.version@
management:
security:
enabled: false
http://localhost:8000/health
http://localhost:8000/info
@RestController
public class MovieController {
private static final Logger logger = LoggerFactory.getLogger(MovieController.class);
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
//return this.restTemplate.getForObject("http://cloud/" + id, User.class);
return this.restTemplate.getForObject("http://localhost:8000/" + id, User.class);
}
}
那么真的完美吗?来分析一下电影微服务的代码, 在 MovieController.java 中:
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject("http://localhost:8000/"+id,User.class ) ;
}
由代码可知, 我们是把提供者的网络地址 (I p 和端口等)硬编码在代码中的, 当然, 也可将其提取到配置文件中去。例如:
user:
userServiceUrl: http://localhost:8000/
代码改为:
@Value("user:userServiceUrl ")
private String userServiceUrl;
@GetMapping("/user/{id}")
public User findById(@PathVariable Long id) {
return this.restTemplate.getForObject(this.userServiceUrl + id, User.class);
}
在传统的应用程序中,一般都是这么做的。然而,这种方式有很多问题。
适用场景有局限: 如果服务提供者的网络地址(I P 和端口)发生了变化, 将会影响服务消费者。
例如,用户微服务的网络地址发生变化,就需要修改电影微服务的配置,并重新发布。这显然是不可取的。
无法动态伸缩:在生产环境中,每个微服务一般都会部署多个实例,从而实现容灾 和负载均衡。
在微服务架构的系统中,还需要系统具备自动伸缩的能力,例如动态增减节点等。硬编码尤法适应这种需求。
通过前文的介绍,我们知道硬编码提供者地址的方式有不少问题。要想解决这些问题, 服务消费者需要一个强大的服务发现机制,服务消费者使用这种机制获取服务提供者的网络信息。不仅如此,即使服务提供者的信息发生变化, 服务消费者也无须修改配置文件。
服务发现组件提供这种能力。在微服务架构中,服务发现组件是一个非常关键的组件。
服务提供者、服务消费者、服务发现组件这三者之间的关系大致如下:
各个微服务在启动时,将自己的网络地址等信息注册到服务发现组件中,服务发现 组件会存储这些信息。
服务消费者可从服务发现组件查询服务提供者的网络地址,并使用该地址调用服务 提供者的接口。
各个微服务与服务发现组件使用一定机制(例如心跳)通信。服务发现组件如长时 间无法与某微服务实例通信,就会注销该实例。
微服务网络地址发生变更(例如实例增减或者 IP 端口发生变化等)时, 会重新注册到服务发现组件。使用这种方式, 服务消费者就无须人工修改提供者的网络地址了。
综上, 服务发现组件应具备以下功能:
服务注册表:是 服务发现组件的核心,它 用 来记录各个微服务的信息, 例如微服务的名称、IP、端口等。服务注册表提供查询API 和管理 API , 查询API 用于查询可用的微服务实例, 管理 API 用于服务的注册和注销。
服务注册与服务发现: 服务注册是指微服务在启动时, 将自己的信息注册到服务发现组件上的过程。服务发现是指查询可用微服务列表及其网络地址的机制。
服务检查: 服务发现组件使用一定机制定时检测已注册的服务, 如发现某实例长时间无法访问, 就会从服务注册表中移除该实例。
综上, 使用服务发现的好处是显而易见的。Spring Cloud 提供了多种服务发现组件的支待, 例如 Eureka、Consul 和 Zookeeper 等。本书将以Eureka 为例, 为大家详细讲解服务注册与发现。
Eureka 包含两个组件: Eureka Server 和 Eureka Client , 它们的作用如下:
Eureka Server 提供服务发现的能力, 各个微服务启动时, 会向 Eureka Server 注册自己的信息(例如 IP、端口、微服务名称等 ), Eureka Server 会存储这些信息。
Eureka Client 是一个 Java客户端,用 于简化与 Eureka Server 的交互。
微服务启动后, 会周期性( 默认 30 秒)地向 Eureka Server 发送心跳以续约自己的“租期”。
如果 Eureka Server 在一定时间内没有接收到某个微服务 实例的心跳, Eureka Server将会注销该实例(默认 90 秒)。
默认情况下, Eureka Server 同时也是Eureka Client。多个 Eureka Server 实例, 互相之间通过复制的方式, 来实现服务注册表中数据的同步。
Eureka Client 会缓存服务注册表中的信息。这种方式有一定的优势一—首先,微服务无须每次请求都查询 Eureka Server, 从而降低了 Eureka Server 的压力;
其次 , 即使Eureka Server 所有节点都宕掉, 服务消费者依然可以使用缓存 中的信息找到服务提供者并完成调用。
综上, Eureka 通过 心跳检查、客户端缓存等机制, 提高了系统的灵活性、可伸缩 性和可用性。
添加eureka-server依赖:
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
完整的pom.xml文件如下:
4.0.0
com.qy
discovery-eureka
0.0.1-SNAPSHOT
jar
discovery-eureka
Demo project for Spring Boot
org.springframework.boot
spring-boot-starter-parent
1.5.9.RELEASE
UTF-8
1.8
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.cloud
spring-cloud-dependencies
Edgware.RELEASE
pom
import
org.springframework.boot
spring-boot-maven-plugin
/*
使用eureka做为服务发现
*/
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryEurekaApplication.class, args);
}
}
server:
port: 8761 # 指定该Eureka实例的端口
enableSelfPreservation: false
eureka:
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://localhost:8761/eureka/
简要讲解一下 application.yml 中的配置属性:
eureka.client.registerWithEureka: 表示是否将自己注册到 Eureka Server , 默认为true。由于当前应用就是EurekaServer, 故而设为false。
eureka.client.fetchRegistry: 表示是否从 Eureka Server 获取注册信息, 默认为true。因为这是一个单点的Eureka Server, 不需要同步其他的Eureka Server 节点的数据, 故而设为false。
eureka.client.serviceUrl.defaultZone : 设置与 EurekaServer 交互的地址, 查询服务和注册服务都需要依赖这个地址。默认是http://localhost:8761/eureka
这样一个 Eureka Server 就编写完成了。
测试
启动 Eureka Server, 访问http://localhost:8761/
Eureka Server 的首页展示了很多信息, 例如当前实例的系统状态、注册到 Eureka Server 上的服务实例、常用信息、实例信息等。显然, 当前还没有任何微服务实例被注册到Eureka Server 上。
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
server:
port: 8010
spring:
application:
name: spring-customer
user:
userServiceUrl: http://localhost:8000/
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
其中, spring.application.name 用于指定注册到 Eureka Server 上的应用名称;
eureka.instance.prefer-ip-address = true 表示将自己的 IP 注册到 Eureka Server 。
如不配置该属性或将其设置为 false , 则表示注册微服务所在操作系统的hostname 到 EurekaServer。
@SpringBootApplication
@EnableEurekaClient
public class Cloud01Application {
public static void main(String[] args) {
SpringApplication.run(Cloud01Application.class, args);
}
}
微服务注册与发现
也可以使用EnableEurekaClient注解替代@EnableDiscoveryClient
这样就可以将用户微服务注册到 Eureka Server 上了。
同理, 将电影微服务也注册到Eureka Server 上,配置电影微服务的 spring.application.name为consmuer-movie
测试