Spring Cloud版——电影售票系统<—>Eureka微服务注册与发现
2017-08-17 00:00
453 查看
摘要: 使用微服务构建的是分布式系统,微服务之间通过网络进行通信。我们使用服务提供者与服务消费者来描述微服务之间的调用关系。服务提供者:服务的被调用方(为其他服务提供服务的服务);服务消费者:服务的调用方(依赖其他服务的服务)。
GitHub地址:https://github.com/leebingbin/SpringCloud.MovieTicketing
一、服务提供者与服务消费者
使用微服务构建的是分布式系统,微服务之间通过网络进行通信。我们使用服务提供者与服务消费者来描述微服务之间的调用关系。
例如,在电影售票系统中,用户向电影微服务发起了一个购票的请求。在进行购票的业务操作前,电影微服务需要调用用户微服务的接口,查询当前用户的余额是多少,是不是符合购票标准等。在这种场景下,用户微服务就是一个服务提供者,电影微服务则是一个服务消费者。
就以该场景,先来编写一个用户微服务,然后编写一个电影微服务。
二、编写服务提供者
编写一个服务提供者(用户微服务),该服务可通过主键查询用户信息。开发测试阶段,使用Spring Data JPA作为持久化层框架,使用H2作为数据库。
创建一个Maven项目,它的ArtifactId是movieticketing-provider-user,对应pom.xml的内容如下:
其中,spring-boot-starter-web提供了SpringMVC的支持;spring-boot-starter-data-jpa提供了Spring Data JPA的支持。
在项目中建立文件夹DB存放schema.sql,内容如下:
创建用户实体类:
创建DAO:
创建Controller:
Tips: Controller中用到的@GetMapping,是Spring4.3提供的新注解。它是一个组合注解,等价于@RequestMapping(meth = RequestMethod.GET),用于简化开发。同理,还有 @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping 等等。
编写启动类,在类上使用@SpringBootApplication声明这是一个Spring Boot项目。
Tips1: @SpringBootApplication 是一个组合注解,它整合了@Configuration、@EnableAutoConfiguration和@ComponentScan注解,并开启了Spring Boot程序的组件扫描和自动配置功能。在开发Spring Boot 程序的过程中,常常会组合使用 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 等注解,所以Spring Boot提供了@SpringBootApplication, 来简化开发。
Tips2: SpringCLoud中的“Discovery Service”有多种实现,比如:eureka, consul, zookeeper。
@EnableDiscoveryClient注解是基于spring-cloud-commons依赖,并且在classpath中实现; @EnableEurekaClient注解是基于spring-cloud-netflix依赖,只能为eureka作用;如果你的classpath中添加了eureka,则它们的作用是一样的。
编写配置文件,命名为application.yml
Tips: 在传统的WebApp开发中,常使用properties格式文件作为配置文件。Spring Boot以及Spring Cloud支持使用properties或者yml格式的文件作为配置文件。yml 文件格式是YAML (Yet Another Markup Language)编写的文件格式,YAML 和 properties 格式的文件可互相转换。YAML 比 properties结构清晰;可读性、可维护性也更强,并且语法非常简洁;但是yml有严格的缩进。
测试访问:http://localhost:8000/2,获得结果如下
说明已通过ID查询用户信息。
三、 编写服务消费者
上节编写了一个服务提供者(用户微服务),本节来编写一个服务消费者(电影微服务)。该服务很简单,使用RestTemplate调用用户微服务的API,从而查询指定id的用户信息。
首先,创建一个Maven项目,ArifactId是movieticketing-consumer-movie。添加依赖如下:
创建用户实体类,该类是一个POJO。
创建启动类。
Tips: @Bean是一个方法注解,作用是实例化一个Bean 并使用该方法的名称命名。例如,添加@Bean注解的restTemplate()方法,等价于RestTemplate restTemplate = new RestTemplate();
创建Controller,在其中使用RestTemplate请求用户微服务的API。
编写配置文件application.yml
至此,一个简单的电影微服务就完成了!
测试,访问:http://127.0.0.1:8010/user/1 ,结果如下:
测试结果,说明电影微服务可以正常使用RestTemplate调用用户微服务的API。
四、为项目整合和使用Spring Boot Actuator
Spring Boot Actuator提供很多监控端点。可使用 http://{ip}:{port}/{endpoint} 的形式访问这些端点,从而了解应用程序的运行状况。
如需为项目整合Actuator,为项目添加以下依赖:
测试访问: http://localhost:8000/health , 如下
Tips: UP表示运行正常。除UP外,DOWN、OUT_OF_SERVICE、UNKNOWN等状态。
五、硬编码的问题
以MovieController.java中:
由此可知,我们把提供者的网络地址(IP和端口等)硬编码在代码中的,当然,也可将其提取到配置文件中去。例如:
代码改为:
在传统的应用程序中,一般都是这么做。但这种方式有很多问题:
1、适用场景有局限:如果服务提供者的网络地址(IP和端口)发生了变化,将会影响服务消费者。
2、无法动态伸缩:在生产环境中,每个微服务一般都会部署多个实例,从而实现容灾和负载均衡。在微服务架构中,还需要系统具备自动伸缩的能力。硬编码无法适应这种需求。
六、微服务注册与发现
硬编码提供者地址的方式有不少问题。要想解决这些问题,服务消费者需要一个强大的服务发现机制,服务消费者使用这种机制获取服务提供者的网络消息。不仅如此,即使服务提供者的消息发生变化,服务消费者也无须修改配置文件。
服务发现组件提供这种能力。在微服务架构中,服务发现组件是一个非常关键的组件。
服务提供者、服务消费者、服务发现组件这三者之间的关系大致如下:
* 各个微服务在启动时,将自己的网络地址等信息注册到服务发现组件中,服务发现组件会存储这些信息。
* 服务消费者可从服务发现组件查询服务提供者的网络地址,并使用该地址调用服务提供者的接口。
* 各个微服务与服务发现组件使用一定的机制(例如,心跳)通信。服务发现组件如长时间无法与某微服务实例通信,就会注销该实例。
* 微服务网络地址发生变更(例如实例增减或者IP端口发生变化等)时,会重新注册到服务发现组件。使用这种方式,服务消费者就无须人工修改提供者的网络地址了。
综上所述,服务发现组件应具备以下功能:
* 服务注册表:是服务发现组件的核心,它用来记录各个微服务的信息(例如微服务的名称、IP、端口等)。服务注册表提供查询API和管理API, 查询API 用于查询可用的微服务实例,管理API用于服务的注册和注销。
* 服务注册与服务发现:服务注册是指微服务在启动时,将自己的信息注册到服务发现组件上的过程。服务发现是指查询可用微服务列表及其网络地址的机制。
* 服务检查:服务发现组件使用一定机制定时检测已注册的服务,如发现某实例长时间无法访问,就会从服务注册表中移除该实例。
Spring Cloud提供了多种服务发现组件的支持,例如Eureka、Consul和Zookeeper等。
Eureka是Netflix开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。
为什么要使用Eureka,因为在一个完整的系统架构中,任何单点的服务都不能保证不会中断,因此我们需要服务发现机制,在某个节点中断后,其它的节点能够继续提供服务,从而保证整个系统是高可用的。
服务发现有两种模式:一种是客户端发现模式,一种是服务端发现模式。Erueka采用的是客户端发现模式。
七、 Rureka简介
Eureka是Netflix开源的服务发现组件,本身是一个基于REST的服务。它包含Server和Client两部分。Spring Cloud将它集成在子项目Spring Cloud Netflix中,从而实现微服务的注册与发现。
分析Eureka的原理之前,先了解一下Region和Availability Zone:
Region 和 Availability Zone均是AWS的概念。其中,Region 表示AWS的地理位置,每个Region都有多个Availability Zone, 各个Region 之间完全隔离。AWS通过这种方式实现了最大的容错和稳定性。
Spring Cloud 默认使用的Region 是us-east-1, 在非AWS环境下,可以将Availability Zone理解为机房,将Region 理解为跨机房的Eureka集群。
该Eureka官方的架构图比较详细地描述了Eurka集群的工作原理:
* Application Service 相当于“电影售票系统”中的服务提供者。
* Application Client 相当于“电影售票系统”中的服务消费者。
* Make Remote Call, 可以理解成调用RESTful API的行为。
* us-east-1c、us-east-1d等都是zone,它们属于us-east-1这个region。
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 通过心跳检查、客户端缓存等机制,提高了系统的灵活性、可伸缩性和可用性。
Tips: eureka.client.healthcheck.enabled=true只能配置在application.yml中,如果配置在bootstrap.yml中,可能会导致一些不良的副作用,如,应用注册到Eureka Server上的状态是UNKNOWN。
当eureka.client.healthcheck.enabled=true时,/pause 端点(该端点由Spring Boot Actuator提供,用于暂停应用)无法正常工作,经测试,发现当eureka.client.healthcheck.enabled=true时,请求 /pause 端点无法将应用在Eureka Server 上的状态标记为DOWN。(Spring Boot: Spring Boot 1.4.3RELEASE ; Spring Cloud : Spring Cloud Camden SR4)该Bug尚未修复。
本文为博主原创文章,转载请注明出处!
https://my.oschina.net/u/3375733/blog/
GitHub地址:https://github.com/leebingbin/SpringCloud.MovieTicketing
一、服务提供者与服务消费者
使用微服务构建的是分布式系统,微服务之间通过网络进行通信。我们使用服务提供者与服务消费者来描述微服务之间的调用关系。
名称 | 定义 |
服务提供者 | 服务的被调用方(为其他服务提供服务的服务) |
服务消费者 | 服务的调用方(依赖其他服务的服务) |
就以该场景,先来编写一个用户微服务,然后编写一个电影微服务。
二、编写服务提供者
编写一个服务提供者(用户微服务),该服务可通过主键查询用户信息。开发测试阶段,使用Spring Data JPA作为持久化层框架,使用H2作为数据库。
创建一个Maven项目,它的ArtifactId是movieticketing-provider-user,对应pom.xml的内容如下:
<?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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bingbinlee.springcloud</groupId> <artifactId>movieticketing-provider-user</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <!-- 引入spring boot的依赖 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.3.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> <!-- 引入spring cloud的依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- 添加spring-boot的maven插件 --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
其中,spring-boot-starter-web提供了SpringMVC的支持;spring-boot-starter-data-jpa提供了Spring Data JPA的支持。
在项目中建立文件夹DB存放schema.sql,内容如下:
drop table user if exists; create table user ( id bigint generated by default as identity, username varchar(40), name varchar(20), age int(3), balance decimal(10,2), primary key(id) );
insert into user (id, username, name, age, balance) values(1, 'user1', "张三", 20, 100.00); insert into user (id, username, name, age, balance) values(2, 'user2', "李四", 30, 200.00); insert into user (id, username, name, age, balance) values(3, 'user3', "王麻子", 40, 300.00);
创建用户实体类:
package com.bingbinlee.springcloud.micro.entity; import javax.persistence.*; import java.math.BigDecimal; @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column private String username; @Column private String name; @Column private Integer age; @Column private BigDecimal balance; public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } public String getUsername() { return this.username; } public void setUsername(String username) { this.username = username; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Integer getAge() { return this.age; } public void setAge(Integer age) { this.age = age; } public BigDecimal getBalance() { return this.balance; } public void setBalance(BigDecimal balance) { this.balance = balance; } }
创建DAO:
package com.bingbinlee.springcloud.micro.repository; import com.bingbinlee.springcloud.micro.entity.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository<User, Long> { }
创建Controller:
package com.bingbinlee.springcloud.micro.controller; import com.bingbinlee.springcloud.micro.entity.User; import com.bingbinlee.springcloud.micro.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @Autowired private UserRepository userRepository; @GetMapping("/{id}") public User findById(@PathVariable Long id){ User findOne = this.userRepository.findOne(id); return findOne; } }
Tips: Controller中用到的@GetMapping,是Spring4.3提供的新注解。它是一个组合注解,等价于@RequestMapping(meth = RequestMethod.GET),用于简化开发。同理,还有 @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping 等等。
编写启动类,在类上使用@SpringBootApplication声明这是一个Spring Boot项目。
package com.bingbinlee.springcloud.micro; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @EnableDiscoveryClient @SpringBootApplication public class ProviderUserApplication { public static void main(String[] args) { SpringApplication.run(ProviderUserApplication.class, args); } }
Tips1: @SpringBootApplication 是一个组合注解,它整合了@Configuration、@EnableAutoConfiguration和@ComponentScan注解,并开启了Spring Boot程序的组件扫描和自动配置功能。在开发Spring Boot 程序的过程中,常常会组合使用 @Configuration、@EnableAutoConfiguration 和 @ComponentScan 等注解,所以Spring Boot提供了@SpringBootApplication, 来简化开发。
Tips2: SpringCLoud中的“Discovery Service”有多种实现,比如:eureka, consul, zookeeper。
@EnableDiscoveryClient注解是基于spring-cloud-commons依赖,并且在classpath中实现; @EnableEurekaClient注解是基于spring-cloud-netflix依赖,只能为eureka作用;如果你的classpath中添加了eureka,则它们的作用是一样的。
编写配置文件,命名为application.yml
server: port: 8000 spring: application: name: movieticketing-provider-user jpa: generate-ddl: false show-sql: true hibernate: ddl-auto: none datasource: # 指定数据源 platform: h2 # 指定数据源类型 schema: classpath:schema.sql # 指定h2数据库的建表脚本 data: classpath:data.sql # 指定h2数据库的数据脚本 logging: # 配置日志级别,让hibernate打印出执行的SQL level: root: INFO org.hibernate: INFO org.hibernate.type.descriptor.sql.BasicBinder: TRACE org.hibernate.type.descriptor.sql.BasicExtractor: TRACE eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ instance: prefer-ip-address: true
Tips: 在传统的WebApp开发中,常使用properties格式文件作为配置文件。Spring Boot以及Spring Cloud支持使用properties或者yml格式的文件作为配置文件。yml 文件格式是YAML (Yet Another Markup Language)编写的文件格式,YAML 和 properties 格式的文件可互相转换。YAML 比 properties结构清晰;可读性、可维护性也更强,并且语法非常简洁;但是yml有严格的缩进。
测试访问:http://localhost:8000/2,获得结果如下
说明已通过ID查询用户信息。
三、 编写服务消费者
上节编写了一个服务提供者(用户微服务),本节来编写一个服务消费者(电影微服务)。该服务很简单,使用RestTemplate调用用户微服务的API,从而查询指定id的用户信息。
首先,创建一个Maven项目,ArifactId是movieticketing-consumer-movie。添加依赖如下:
<?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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bingbinlee.springcloud</groupId> <artifactId>movieticketing-consumer-movie</artifactId> <packaging>jar</packaging> <version>1.0-SNAPSHOT</version> <!-- 引入spring boot的依赖 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.3.RELEASE</version> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> <!-- 引入spring cloud的依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <!-- 添加spring-boot的maven插件 --> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
创建用户实体类,该类是一个POJO。
package com.bingbinlee.springcloud.micro.entity; import java.math.BigDecimal; public class User { private Long id; private String username; private String name; private Integer age; private BigDecimal balance; public Long getId() { return this.id; } public void setId(Long id) { this.id = id; } public String getUsername() { return this.username; } public void setUsername(String username) { this.username = username; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } public Integer getAge() { return this.age; } public void setAge(Integer age) { this.age = age; } public BigDecimal getBalance() { return this.balance; } public void setBalance(BigDecimal balance) { this.balance = balance; } }
创建启动类。
package com.bingbinlee.springcloud.micro; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class ConsumerMovieApplication { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(ConsumerMovieApplication.class, args); } }
Tips: @Bean是一个方法注解,作用是实例化一个Bean 并使用该方法的名称命名。例如,添加@Bean注解的restTemplate()方法,等价于RestTemplate restTemplate = new RestTemplate();
创建Controller,在其中使用RestTemplate请求用户微服务的API。
package com.bingbinlee.springcloud.micro.controller; import com.bingbinlee.springcloud.micro.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class MovieController { @Autowired private RestTemplate restTemplate; @GetMapping("/user/{id}") public User findById(@PathVariable Long id) { return this.restTemplate.getForObject("http://localhost:8000/" + id, User.class); } }
编写配置文件application.yml
server: port: 8010 spring: application: name: movieticketing-consumer-movie #eureka: # client: # serviceUrl: # defaultZone: http://localhost:8761/eureka/ # instance: # prefer-ip-address: true
至此,一个简单的电影微服务就完成了!
测试,访问:http://127.0.0.1:8010/user/1 ,结果如下:
测试结果,说明电影微服务可以正常使用RestTemplate调用用户微服务的API。
四、为项目整合和使用Spring Boot Actuator
Spring Boot Actuator提供很多监控端点。可使用 http://{ip}:{port}/{endpoint} 的形式访问这些端点,从而了解应用程序的运行状况。
端点 | 描述 | HTTP方法 |
autoconfig | 显示自动配置的信息 | GET |
beans | 显示应用程序上下文所有的Spring bean | GET |
configprops | 显示所有@ConfigurationProperties的配置属性列表 | GET |
dump | 显示线程活动的快照 | GET |
env | 显示应用的环境变量 | GET |
health | 显示应用程序的健康指标,这些值由HealthIndicator的实现类提供 | GET |
info | 显示应用的信息,可使用info.*属性自定义info端点公开的数据 | GET |
mapping | 显示所有的URL路径 | GET |
metrics | 显示应用的度量标准信息 | GET |
shutdown | 关闭应用(默认情况下不启用,如需启动,需设置endpoints.shutdown.enabled=true) | POST |
trace | 显示跟踪信息(默认情况下为最近100个HTTP请求) | GET |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
测试访问: http://localhost:8000/health , 如下
Tips: UP表示运行正常。除UP外,DOWN、OUT_OF_SERVICE、UNKNOWN等状态。
五、硬编码的问题
以MovieController.java中:
@GetMapping("/user/{id}") public User findById(@PathVariable Long id) { return this.restTemplate.getForObject("http://localhost:8000/" + id, User.class); }
由此可知,我们把提供者的网络地址(IP和端口等)硬编码在代码中的,当然,也可将其提取到配置文件中去。例如:
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); }
在传统的应用程序中,一般都是这么做。但这种方式有很多问题:
1、适用场景有局限:如果服务提供者的网络地址(IP和端口)发生了变化,将会影响服务消费者。
2、无法动态伸缩:在生产环境中,每个微服务一般都会部署多个实例,从而实现容灾和负载均衡。在微服务架构中,还需要系统具备自动伸缩的能力。硬编码无法适应这种需求。
六、微服务注册与发现
硬编码提供者地址的方式有不少问题。要想解决这些问题,服务消费者需要一个强大的服务发现机制,服务消费者使用这种机制获取服务提供者的网络消息。不仅如此,即使服务提供者的消息发生变化,服务消费者也无须修改配置文件。
服务发现组件提供这种能力。在微服务架构中,服务发现组件是一个非常关键的组件。
服务提供者、服务消费者、服务发现组件这三者之间的关系大致如下:
* 各个微服务在启动时,将自己的网络地址等信息注册到服务发现组件中,服务发现组件会存储这些信息。
* 服务消费者可从服务发现组件查询服务提供者的网络地址,并使用该地址调用服务提供者的接口。
* 各个微服务与服务发现组件使用一定的机制(例如,心跳)通信。服务发现组件如长时间无法与某微服务实例通信,就会注销该实例。
* 微服务网络地址发生变更(例如实例增减或者IP端口发生变化等)时,会重新注册到服务发现组件。使用这种方式,服务消费者就无须人工修改提供者的网络地址了。
综上所述,服务发现组件应具备以下功能:
* 服务注册表:是服务发现组件的核心,它用来记录各个微服务的信息(例如微服务的名称、IP、端口等)。服务注册表提供查询API和管理API, 查询API 用于查询可用的微服务实例,管理API用于服务的注册和注销。
* 服务注册与服务发现:服务注册是指微服务在启动时,将自己的信息注册到服务发现组件上的过程。服务发现是指查询可用微服务列表及其网络地址的机制。
* 服务检查:服务发现组件使用一定机制定时检测已注册的服务,如发现某实例长时间无法访问,就会从服务注册表中移除该实例。
Spring Cloud提供了多种服务发现组件的支持,例如Eureka、Consul和Zookeeper等。
Eureka是Netflix开发的服务发现框架,SpringCloud将它集成在自己的子项目spring-cloud-netflix中,实现SpringCloud的服务发现功能。
为什么要使用Eureka,因为在一个完整的系统架构中,任何单点的服务都不能保证不会中断,因此我们需要服务发现机制,在某个节点中断后,其它的节点能够继续提供服务,从而保证整个系统是高可用的。
服务发现有两种模式:一种是客户端发现模式,一种是服务端发现模式。Erueka采用的是客户端发现模式。
七、 Rureka简介
Eureka是Netflix开源的服务发现组件,本身是一个基于REST的服务。它包含Server和Client两部分。Spring Cloud将它集成在子项目Spring Cloud Netflix中,从而实现微服务的注册与发现。
分析Eureka的原理之前,先了解一下Region和Availability Zone:
Region 和 Availability Zone均是AWS的概念。其中,Region 表示AWS的地理位置,每个Region都有多个Availability Zone, 各个Region 之间完全隔离。AWS通过这种方式实现了最大的容错和稳定性。
Spring Cloud 默认使用的Region 是us-east-1, 在非AWS环境下,可以将Availability Zone理解为机房,将Region 理解为跨机房的Eureka集群。
该Eureka官方的架构图比较详细地描述了Eurka集群的工作原理:
* Application Service 相当于“电影售票系统”中的服务提供者。
* Application Client 相当于“电影售票系统”中的服务消费者。
* Make Remote Call, 可以理解成调用RESTful API的行为。
* us-east-1c、us-east-1d等都是zone,它们属于us-east-1这个region。
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 通过心跳检查、客户端缓存等机制,提高了系统的灵活性、可伸缩性和可用性。
Tips: eureka.client.healthcheck.enabled=true只能配置在application.yml中,如果配置在bootstrap.yml中,可能会导致一些不良的副作用,如,应用注册到Eureka Server上的状态是UNKNOWN。
当eureka.client.healthcheck.enabled=true时,/pause 端点(该端点由Spring Boot Actuator提供,用于暂停应用)无法正常工作,经测试,发现当eureka.client.healthcheck.enabled=true时,请求 /pause 端点无法将应用在Eureka Server 上的状态标记为DOWN。(Spring Boot: Spring Boot 1.4.3RELEASE ; Spring Cloud : Spring Cloud Camden SR4)该Bug尚未修复。
本文为博主原创文章,转载请注明出处!
https://my.oschina.net/u/3375733/blog/
相关文章推荐
- Spring Cloud版——电影售票系统<五>Hystrix使用消息中间件RabbitMQ收集数据
- Spring Cloud版——电影售票系统<八>使用 Spring Sleuth 实现微服务追踪
- Spring Cloud版——电影售票系统<六>使用 Spring Cloud Config 统一管理微服务配置
- Spring Cloud(二):服务注册与发现Eureka
- Spring Cloud构建微服务架构:服务注册与发现(Eureka、Consul)【Dalston版】
- Spring Cloud Eureka服务注册与发现
- SpringCloud——服务注册与发现Eureka以及注册源码解析
- 【微服务架构】SpringCloud之Eureka(服务注册和服务发现基础篇)(二)
- SpringCloud 教程 | 第一篇: 服务的注册与发现(Eureka)
- 【微服务架构】SpringCloud之Eureka(服务注册和服务发现基础篇)(二)
- Spring Cloud构建微服务架构:Eureka服务注册与发现
- eclipse springcloud服务发现服务注册(Eureka) 学习日志
- SpringCloud 进阶之Eureka(服务注册和发现)
- Spring cloud Eureka服务注册及发现(二)创建注册服务
- springcloud(第三篇)springcloud eureka 服务注册与发现 *****
- 【Spring Cloud】--SpringCloud的服务注册与发现【Eureka】
- 【微服务架构】SpringCloud之Eureka(服务注册和服务发现基础篇)(二)
- 玩转SpringCloud(F版本) 一.服务的注册与发现(Eureka)
- Spring Cloud eureka 注册服务发现,访问管理页面是xml问题解决
- springcloud(第一篇)springcloud eureka 服务注册与发现