前一篇文章《微服务操做模型》中,咱们定义了微服务使用的操做模型。这篇文章中,咱们将开始使用Spring Cloud和Netflix OSS实现这一模型,包含核心部分:服务发现(Service Discovery)、动态路由(Dynamic Routing)、负载均衡(Load Balancing),和边缘服务器(Edge Server),其余部分在后面的文章中介绍。html
咱们将使用来自Spring Cloud和Netflix OSS的一些核心组件,实如今已部署的微服务交互,没必要手动管理配置,如每个微服务的端口或者手工配置路由规则等等。为了不端口冲突,咱们的微服务在启动时,将从端口段中动态获取可用的端口。为了方便访问微服务,咱们将使用Edge Server提供一个微服务的访问入口点。java
在简要介绍Spring Cloud和Netflix OSS组件以后,咱们将描述本系列文章使用的系统,以及如何访问源代码,并编译。同时,也会简要指出源代码中的最重要部分。最后,咱们将运行一些访问服务的测试代码,也会演示如何简单地建立一个新的服务实例,获取并使用负载均衡,全部这一些都没必要手工配置。git
1. Spring Cloud和Netflix OSSgithub
Spring Cloud是spring.io家庭的一个新项目,包含一系列组件,可用来实现咱们的操做模型。很大程度上而言,Spring Cloud 1.0 是基于Netflix OSS组件。在Spring环境中,Spring Cloud 很是友好地集成了Netflix 组件,使用了和Spring Boot类似的自动配置和惯例优于配置。web
下表映射了操做模式中介绍的组件和咱们将要使用的实际组件:spring
本文将包含Eureka、Ribbon和Zuul:shell
1/Netflix Eureka – Service Discover Server服务发现json
Netflix Eureka容许微服务在运行时自我注册安全
2/Netflix Ribbon-Dynamic Routing and Load Balancer动态路由和负载均衡服务器
Netflix Ribbon能够在服务消费方运行时查询微服务。Ribbon使用Eureka中的信息定位合适的服务实例。若是发现了多个服务实例,Ribbon将应用负载均衡来转发请求到可用的微服务实例。Ribbon 不做为一个单独的服务运行,而是嵌入在每个服务消费方中。
3/Netflix Zuul – Edge Server边缘服务器
Zuul是咱们对外部世界的守门员,禁止任一未受权的外部请求进入。Zuul在系统内部也提供了方便的进入入口点。经过使用动态分配的端口,能够避免端口冲突,以及最小化管理成本,可是也致使服务消费方更难接入。Zuul 使用Ribbon来查询可用的服务,并路由外部的请求到合适的服务实例。在本文中,咱们将仅仅使用Zuul提供了便利的访问入口点,安所有分在下一篇文章中讨论。
备注:经过边缘服务器(Edge Server),可被外部访问的微服务,在系统中可称为API。
2. 系统架构
为了测试这些组件,咱们须要一个可实施的业务系统。本文章的目标是开发实现以下系统:
上图包含4个业务服务(绿色文本框):
1/ 三个核心服务负责处理信息:产品、推荐和评论;
2/ 一个组合服务 product-composite,用来聚合3个核心服务的信息,组合包含评论和推荐的产品信息视图;
为了支持业务服务,咱们使用了以下基础设施服务和组件(蓝色文本框):
1/ 服务发现服务器(Service Discovery Server – Netflix Eureka)
2/ 动态路由和负载均衡(Netflix Ribbon)
3/ 边缘服务器(Edge Server – Netflix Zuul)
为了强调微服务和单体应用的差别,咱们将每个服务运行在单独的微服务进程中。在一个大系统中,如此细粒度的微服务可能并不方便。相应地,一组相关的微服务可能合并为一组,保持微服务的数量在可管理的水平,但这并非退回到巨大的单体应用。
3. 获取源代码并编译
获取源代码,并进行测试,须要已安装Java SE8和Git,接着执行以下操做:
$ git clone https://github.com/callistaenterprise/blog-microservices.git
$ cd blog-microservices
$ git checkout -b B1 M1.1
将生成以下的目录结构:
每个组件独立编译(记住咱们再也不编译单体应用),所以每个组件都有本身的build文件。咱们使用Gradle编译系统,若是你没有安装Gradle,build文件将自动下载。为了简化编译过程,咱们提供了一个小的shell脚本,可用来编译组件:
$ ./build-all.sh
若是在Windows环境下,你能够执行相应的bat文件 build-all.bat。
将显示6个log消息,并显示:BUILD SUCCESSFUL
4. 阅读源代码
快速看看关键的源代码,每个微服务开发为一个独立的Spring Boot应用,并使用Undertow(一个轻量级的Servlet 3.1容器)做为web server。Spring MVC用来实现 REST-based服务,Spring RestTemplate 用来执行外部调用。若是你想更多地了解这些核心技术,你能够查看相关的文章。
这里,咱们关注如何使用Spring Cloud和Netflix OSS功能。
备注:为了让源码易于理解,咱们特地让实现尽可能简单。
4.1 Gradle依赖
本着Spring Boot的精髓,Spring Cloud定义了一组starter 依赖,便于引入须要的特定依赖。为了在微服务中使用Eureka和Ribbon,以及方便调用其余微服务,在build文件中添加以下:
compile("org.springframework.cloud:spring-cloud-starter-eureka:1.0.0.RELEASE")
能够查看product-service/build.gradle 获取完整的例子。
为了搭建Eureka 服务器,添加以下依赖:
compile('org.springframework.cloud:spring-cloud-starter-eureka-server:1.0.0.RELEASE')
完整的例子,能够查看discovery-server/build.gradle。
4.2 基础设施服务器
基于Spring Cloud和Netflix OSS搭建基础设施服务器至关方便。例如,在一个标准的Spring Boot应用中,添加@EnableEurekaServer标注来搭建Eureka 服务器。
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
完整的实例,能够查看EurekaApplication.java代码。
搭建Zuul 服务器,能够添加@EnableZuulProxy标注。完整的实例,能够查看ZuulApplication.java代码。
经过这些简单的标注,能够搭建一个默认的服务器配置。根据须要,也能够经过特定的设置覆盖默认配置。例如,咱们能够经过覆盖默认的配置,限制边缘服务器容许路由调用的微服务。默认状况下,Zuul搭建了Eureka中能够发现的每个微服务的路由。经过以下的application.yml配置,限制了只容许访问组合服务-product service的路由。
zuul:
ignoredServices: "*"
routes:
productcomposite:
path: /productcomposite/**
查看edge-server/application.yml获取完整的例子。
4.3 业务服务
经过在Spring Boot应用中,添加@EnableDiscoveryClient标注,自动注册微服务到Eureka Server中。
@SpringBootApplication @EnableDiscoveryClient public class ProductServiceApplication { public static void main(String[] args) { SpringApplication.run(ProductServiceApplication.class, args); } }
完整的例子,能够查看ProductServiceApplication.java。
为了查询和调用微服务实例,可使用Ribbon和Spring RestTemplate,以下所示:
@Autowired private LoadBalancerClient loadBalancer; ... public ResponseEntity<List<Recommendation>> getReviews(int productId) { ServiceInstance instance = loadBalancer.choose("review"); URI uri = instance.getUri(); ... response = restTemplate.getForEntity(url, String.class);
服务消费方只须要知道服务的名字,如上述例子中的review,Ribbon(LoadBalancerClient类)将发现服务实例,并返回URI给服务消费方。
5. 启动系统
在本文中,咱们将在本地开发环境中做为一个java进程来启动微服务。在接下来的文章中,咱们将描述如何部署微服务到云环境和Docker容器中。
为了运行下面的一些命令,须要安装curl和jq工具。
每个微服务使用命令 ./gradlew bootRun 来启动。
首先,启动微服务基础设施,如:
$ cd .../blog-microservices/microservices
$ cd support/discovery-server; ./gradlew bootRun
$ cd support/edge-server; ./gradlew bootRun
一旦启动了上述基础设施微服务,接着启动业务微服务:
$ cd core/product-service; ./gradlew bootRun
$ cd core/recommendation-service; ./gradlew bootRun
$ cd core/review-service; ./gradlew bootRun
$ cd composite/product-composite-service; ./gradlew bootRun
若是在Windows环境下,能够运行相应的bat文件,start-all.bat文件。
一旦微服务启动了,将自注册到服务发现服务器(Service Discovery Server - Eureka)中去,并输出以下日志:
DiscoveryClient ... - registration status: 204
在服务发现web 应用中,能够看到以下4个业务服务,和边缘服务器(Edge Server)(http://localhost:8761):
为了了解上述服务的更多信息,如使用的ip地址和端口,可以使用Eureka REST API:
$ curl -s -H "Accept: application/json" http://localhost:8761/eureka/apps | jq '.applications.application[] | {service: .name, ip: .instance.ipAddr, port: .instance.port."$"}' { "service": "PRODUCT", "ip": "192.168.0.116", "port": "59745" } { "service": "REVIEW", "ip": "192.168.0.116", "port": "59178" } { "service": "RECOMMENDATION", "ip": "192.168.0.116", "port": "48014" } { "service": "PRODUCTCOMPOSITE", "ip": "192.168.0.116", "port": "51658" } { "service": "EDGESERVER", "ip": "192.168.0.116", "port": "8765" }
如今,咱们已经准备好进行测试了。首先,验证能够到达咱们的微服务,接着,咱们建立一个新的微服务实例,并经过Ribbon在多个服务实例上实施负载均衡。
备注:在接下来的文章中,咱们也会尝试失败的场景,演示电路断路器(Circuit Breaker)是如何工做的。
5.1 开始测试
经过边缘服务器来调用组合服务,边缘服务器在端口8765(查看application.yml文件)。咱们经过边缘服务器,以及路径/productcomposite/** 可到达 productcomposite 服务。返回的组合响应以下:
$ curl -s localhost:8765/productcomposite/product/1 | jq . { "name": "name", "productId": 1, "recommendations": [ { "author": "Author 1", "rate": 1, "recommendationId": 1 }, ... ], "reviews": [ { "author": "Author 1", "reviewId": 1, "subject": "Subject 1" }, ... ], "weight": 123 }
若是在微服务内部,咱们实际上能够直接调用微服务,没必要经过边缘服务器。固然,问题是咱们不知道服务运行在什么端口,由于服务是动态分配的。可是,咱们能够查看调用Eureka REST API 的输出,就知道服务监听的端口了。咱们可使用以下的命令调用3个核心服务(端口号采用Eureka REST API输出的端口信息):
$ curl -s localhost:51658/product/1 | jq . $ curl -s localhost:59745/product/1 | jq . $ curl -s localhost:59178/review?productId=1 | jq . $ curl -s localhost:48014/recommendation?productId=1 | jq .
在本身的环境中,使用相应的端口号。
5.2 动态负载均衡
为了不服务故障或者临时的网络问题,一般须要多个服务实例,经过负载均衡分发请求。由于咱们使用动态分配的端口和服务发现Server,能够很是容易添加新的服务实例。例如,能够简单启动一个新的review服务,动态分配一个新的端口,并自我注册到服务发现服务器(Service Discovery Server)中。
$ cd .../blog-microservices/microservices/core/review-service
$ ./gradlew bootRun
稍等片刻,第二个服务实例出如今服务发现web应用中(http://localhost:8761):
若是你运行以前的curl命令屡次(curl -s localhost:8765/productcomposite/product/1 | jq .),查看2个review实例的log日志,能够发现负载均衡在2个实例之间自动处理调用请求,没必要手工配置。
6. 总结
咱们已经了解到Spring Cloud和Netflix OSS 组件是如何用来简化独立部署微服务协同工做的,没必要人工管理每个微服务的端口,或者人工配置路由规则。当新的实例启动以后,它们会自动被服务发现Server监测到,并经过负载均衡来接收请求。经过使用边缘服务器(Edge Server),咱们能够控制什么微服务暴露给外部消费方,创建系统的API。
7. 下一步
OK,完成测试以后。接下来还有一些问题没有回答,例如:
1/ 发生故障将如何处理,如出现一个失败的微服务;
2/ 如何阻止对API的未受权访问;
3/ 如何获知微服务内部的运行图,例如为何订单#123456没有交付?
在接下来的文章中,咱们将了解如何使用电路断路器(Circuit Breaker)来提高服务弹性,使用OAuth 2 限制外部访问等等。也将了解如何使用ELK技术栈来收集全部微服务的日志,并呈现日志信息。
原文英文连接:Building microservices with Spring Cloud and Netflix OSS, part 1
译者:Rickie(RickieChina#hotmail*com)