概述
- 认识microservices
- 设置service registry(服务注册中心)
- Registering 和 discovering services(注册和发现服务)
认识microservices
1. 传统的Monoliths application(单一应用)的特点
- *复杂: * 传统单一应用代码库越大,理解整个应用的每个组件就越困难
- *更难测试: * 随着应用的发展,全面的测试更加困难
- 更容易出现库冲突
- *伸缩性问题: * 扩展更困难,效率低
- *技术决策要根据整体: * 整个应用选了语言框架等,新的决策只能在此基础上选择
- 开发周期更长
2. Microservice architecture(微服务架构)特点
*简单: *微服务是一种小的应用,相比传统的单一应用更简单
*容易测试: * 应用越小越容易测试
不太容易出现库冲突
*良好的伸缩性: *更容易扩展
每个微服务都可以选择不同的技术,不需要根据整体进行决策
*开发方便: *可以频繁的将其发布到生产环境
**缺点: ** 远程调用会增加网络延迟
设置service registry(服务注册中心)
1. 认识Eureka
Spring Cloud由几个独立的子项目组成,每个子项目都以某种方式支持微服务开发,其中一个子项目就是Spring Cloud Netflix,其中包含了一个组件
Eureka
,即Netflix service registry所有微服务将通过service registry(Eureka)发现彼此
Eureka
作为微服务应用的所有服务的中央注册中心,其本身也是一个微服务,由于其特性,最好创建在其他服务创建之前
2. Ribbon
由上图可以看出,
other-service
决定使用哪一个some-service
,更好的方式是使用一种客户端负载均衡(load-balancing)算法来决定,这就是Ribbon
的作用Ribbon
是一个客户端负载均衡器,它代表other-service
进行选择some-service
与单一的集中服务进行负载均衡不同,
Ribbon
客户端负载均衡器,对于每一个发送请求的客户端都是本地的对比集中式的负载均衡器,
Ribbon
有一些优势,因为每一个客户端本地都有一个负载均衡器,因此,可以根据不同的客户端使用不同的负载均衡算法,并且可以根据客户端数量伸缩
3. 开始创建
- 使用
Spring Initializr
创建一个新的project,引入Eureka Server
即可,生成Maven依赖如下
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
- 默认使用
spring-cloud.version
版本,也可以自定义版本
<properties>
...
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
- Spring启动器增加注解
@EnableEurekaServer
@SpringBootApplication
@EnableEurekaServer
public class ServiceRegistryApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceRegistryApplication.class, args);
}
}
Eureka
默认监听的是8080端口,项目启动之后从浏览器打开http://localhost:8080
即可查看Eureka
详情Eureka
还提供了一个REST API,通过http://localhost:8080/eureka/apps
可以获取注册中心的所有服务实例<applications> <versions__delta>1</versions__delta> <apps__hashcode/> </applications>
4. 配置Eureka
基本配置
application.yml
eureka: instance: hostname: localhost client: #开发环境下如果就一个eureka实例,则关闭下列选项 fetch-registry: false #默认为true,eureka可以从其他eureka实例上获取注册表 register-with-eureka: false #默认为true,在其他eureka实例上将自己注册为服务 service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka server: enable-self-preservation: false #默认为true,默认情况下要求服务实例注册自己,且毎30秒给eureka发送注册更新信息 #如果超过90秒没有收到服务续期,那么会取消该实例注册,进入自我保护模式 #生产环境下: 应开启此模式,设置为true server: port: 8080 #默认端口
虽然单个
Eureka
实例在开发环境下很便利,但是在生产环境下为了高可用性,可能需要配置多个*方式一: * Spring Cloud Services 提供了
Eureka
生产环境下的实施,只需要一个configuration server
和 一个circuit breaker dashboard
即可*方式二: * 最简单的方式直接使用
profiles
属性配置两个Eureka
实例,分别启动两个Eureka
服务即可spring: profiles: eureka-1 application: name: eureka-1 server: port: 8761 eureka: instance: hostname: localhost client: service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka --- spring: profiles: eureka-2 application: name: eureka-2 server: port: 8762 eureka: instance: hostname: localhost client: service-url: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
Registering 和 discovering services(注册和发现服务)
1. 概述
如果没有服务注册,那么
Eureka Service registry
是无用的,如果other services
想要发现并使用你的服务,需要你的服务作为客户端注册到service registry
即service registry client
为了使你的服务允许作为
service registry client
,需要引入Eureka client
依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
Eureka client
依赖项中提供了发现服务所需要的内容,包含Eureka's client-side libiary
和Robbin load balancer
只需要添加此依赖项,将启用你的应用作为
Eureka service registry
的一个客户端,当应用启动时,默认情况下它将尝试去连接运行与本地的Eureka server
并将自己注册到Eureka service registry
中,默认名字为UNKNOWN
2. 配置Eureka client
配置在
Eureka
中的注册名(即给应用定义名称):不设置的情况下为UNKNOWN
spring: application: name: ingredient-service
设置随机端口,防止端口冲突
server: port: 0 #启动时会随机使用可用的端口
生产环境下地址配置:(可能不是localhost,并且配置多个,如果向第一个注册失败,可以改向第二个注册,更可靠)
eureka: client: service-url: defaultZone: http://eureka1.tacocloud.com:8761/eureka/, http://eureka2.tacocloud.com:8762/eureka/
3. 使用服务
- 服务在
Eureka
中注册成功之后,other services
就可以发现并使用它- Spring Cloud提供了
Ribbon
客户端负载均衡策略进行服务的选择,因此无需考虑多个相同服务选择问题- 从
Eureka
中使用服务有两种方式:
- load-balanced RestTemplate / load-balanced WebClient(响应式编程)
- Feign-generated client interfaces
3.1 通过RestTemplate使用服务
创建load-balanced RestTemplate的bean
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
注入bean并使用
@Component public class IngredientServiceClient { private RestTemplate rest; public IngredientServiceClient(@LoadBalanced RestTemplate rest) { this.rest = rest; } //使用 public Ingredient getIngredientById(String ingredientId) { return rest.getForObject( "http://ingredient-service/ingredients/{id}", //此处没有URL硬编码方式,而是使用应用注册名 Ingredient.class, ingredientId); } }
3.2 通过WebClient方式使用服务(针对响应式编程)
创建bean
@Bean @LoadBalanced public WebClient.Builder webClientBuilder() { return WebClient.builder(); }
注入并使用bean
@Component public class IngredientServiceClient { private WebClient.Builder wcBuilder; public IngredientServiceClient( @LoadBalanced WebClient.Builder webclientBuilder wcBuilder) { this.wcBuilder = wcBuilder; } public Mono<Ingredient> getIngredientById(String ingredientId) { return wcBuilder.build() .get() .uri("http://ingredient-service/ingredients/{id}", ingredientId) .retrieve().bodyToMono(Ingredient.class); } }
3.3 使用Feign创建基于接口的服务客户端
Feign
是一个客户端库,通过一种独特的丶接口驱动的方法来定义一个REST客户端,简单的说就是通过此库可以很方便的创建使用REST API的客户端引入Maven依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
启用配置
Configuration
@Configuration @EnableFeignClients public RestClientConfiguration { }
创建客户端接口(启动时Feign会自动实现该接口)
@FeignClient("ingredient-service") public interface IngredientClient { @GetMapping("/ingredients/{id}") Ingredient getIngredient(@PathVariable("id") String id); }
使用定义的客户端
@Controller @RequestMapping("/ingredients") public class IngredientController { private IngredientClient client; @Autowired public IngredientController(IngredientClient client) { this.client = client; } @GetMapping("/{id}") public String ingredientDetailPage(@PathVariable("id") String id, Model model) { model.addAttribute("ingredient", client.getIngredient(id)); return "ingredientDetail"; } }