前一篇文章《微服务操作模型》中,我们定义了微服务使用的操作模型。这篇文章中,我们将开始使用Spring Cloud和Netflix OSS实现这一模型,包含核心部分:服务发现(Service Discovery)、动态路由(Dynamic Routing)、负载均衡(Load Balancing),和边缘服务器(Edge Server),其他部分在后面的文章中介绍。

我们将使用来自Spring Cloud和Netflix OSS的一些核心组件,实现在已部署的微服务交互,不必手动管理配置,如每一个微服务的端口或者手工配置路由规则等等。为了避免端口冲突,我们的微服务在启动时,将从端口段中动态获取可用的端口。为了方便访问微服务,我们将使用Edge Server提供一个微服务的访问入口点。

在简要介绍Spring Cloud和Netflix OSS组件之后,我们将描述本系列文章使用的系统,以及如何访问源代码,并编译。同时,也会简要指出源代码中的最重要部分。最后,我们将运行一些访问服务的测试代码,也会演示如何简单地创建一个新的服务实例,获取并使用负载均衡,所有这一些都不必手工配置。

1. Spring Cloud和Netflix OSS

Spring Cloud是spring.io家庭的一个新项目,包含一系列组件,可用来实现我们的操作模型。很大程度上而言,Spring Cloud 1.0 是基于Netflix OSS组件。在Spring环境中,Spring Cloud 非常友好地集成了Netflix 组件,使用了和Spring Boot相似的自动配置和惯例优于配置。

下表映射了操作模式中介绍的组件和我们将要使用的实际组件:

本文将包含Eureka、Ribbon和Zuul:

1/Netflix Eureka – Service Discover Server服务发现

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)

基于Spring Cloud和Netflix OSS 构建微服务-Part 1的更多相关文章

  1. 基于Spring Cloud和Netflix OSS构建微服务,Part 2

    在上一篇文章中,我们已使用Spring Cloud和Netflix OSS中的核心组件,如Eureka.Ribbon和Zuul,部分实现了操作模型(operations model),允许单独部署的微 ...

  2. 在IDEA编辑器中建立Spring Cloud的子项目包(构建微服务)

    本文介绍在IDEA编辑器中建立Spring Cloud的子项目包 总共分为5个包: 外层使用maven quickstart建立,子modules直接选择了springboot

  3. Building microservices with Spring Cloud and Netflix OSS, part 2

    In Part 1 we used core components in Spring Cloud and Netflix OSS, i.e. Eureka, Ribbon and Zuul, to ...

  4. 今天介绍一下自己的开源项目,一款以spring cloud alibaba为核心的微服务架构项目,为给企业与个人提供一个零开发基础的微服务架构。

    LaoCat-Spring-Cloud-Scaffold 一款以spring cloud alibab 为核心的微服务框架,主要目标为了提升自己的相关技术,也为了给企业与个人提供一个零开发基础的微服务 ...

  5. spring cloud 入门,看一个微服务框架的「五脏六腑」

    Spring Cloud 是一个基于 Spring Boot 实现的微服务框架,它包含了实现微服务架构所需的各种组件. 注:Spring Boot 简单理解就是简化 Spring 项目的搭建.配置.组 ...

  6. Spring Cloud(1):微服务简介

    架构的演进: 1.十年前:用户->单一服务器->单一数据库(支持十万级用户) 2.五年前:用户->负载均衡器->多台服务器->缓存集群->主从数据库(支持百万级用户 ...

  7. SpringCloud(10)使用Spring Cloud OAuth2和JWT保护微服务

    采用Spring Security AOuth2 和 JWT 的方式,避免每次请求都需要远程调度 Uaa 服务.采用Spring Security OAuth2 和 JWT 的方式,Uaa 服务只验证 ...

  8. Spring Cloud(Dalston.SR5)--Zuul 网关-微服务集群

    通过 url 映射的方式来实现 zuul 的转发有局限性,比如每增加一个服务就需要配置一条内容,另外后端的服务如果是动态来提供,就不能采用这种方案来配置了.实际上在实现微服务架构时,服务名与服务实例地 ...

  9. 使用Spring Cloud OAuth2和JWT保护微服务

    采用Spring Security AOuth2 和 JWT 的方式,避免每次请求都需要远程调度 Uaa 服务.采用Spring Security OAuth2 和 JWT 的方式,Uaa 服务只验证 ...

随机推荐

  1. Java线程:什么是线程

    一 基本概念 多任务:同一时刻运行多个程序的能力.每一个任务称为一个线程.可以同时运行一个以上线程的程序称为多线程程序. Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多 ...

  2. ejb ql 返回object

    String sqlStr="select t.car_kind,count(t) from table1 t where t.jb_date='"+jb_date+"' ...

  3. Spring classPath:用法

    http://blog.csdn.net/xing_sky/article/details/8228305 参考文章地址: http://hi.baidu.com/huahua035/item/ac8 ...

  4. SQL server 定时自动执行SQL存储过程

    当一个存储过程是为了生成报表,并且是周期性的,则不需要人工干预,由SQL作业定时自动执行些SQL存储过程即可. 本示例,假设已需要定时执行的存储过程为:Pr_test 工具/原料 SQL Server ...

  5. Flex中操作XML的E4X方法

    用于处理 XML 的 E4X 方法 Flash Player 9 和更高版本,Adobe AIR 1.0 和更高版本   ECMAScript for XML 规范定义了一组用于使用 XML 数据的类 ...

  6. XML之XPath操作

    在学习XPath之前你应该对XML的节点,元素,属性,原子值(文本),处理指令,注释,根节点(文档节点),命名空间以及对节点间的关系如:父(Parent),子(Children),兄弟(Sibling ...

  7. loadrunner:从数据库中取值进行参数化

    下面我们介绍用数据库中的用户名来参数化登陆用户名. 框选住登陆名,点鼠标右键,弹出对话框,选择"替换为新参数"弹出对话框,此时参数名输入:name,参数类型选择File,如图 点& ...

  8. shell 快速入门

    1: 脚本开始行 #!/bin/bash 这一行表明,不管用户选择的是那种交互式shell,该脚本需要使用bash shell来运行. 由于每种shell的语法大不相同,所以这句非常重要. 2:变量 ...

  9. 蓝牙连接音响问题(android电视)

    最近老大让我开发电视的蓝牙,由于android电视的蓝牙不稳定和设计上的各种各样的要求,需要在原有的基础上做一些更改,中间遇到了各种问题,在此总结一下. 我们首先要获取blueToothAdapter ...

  10. 设置npm安装模块目录<nodejs>

    nodejs安装模块命令: npm install <input_name> # 本地安装 npm install <input_name> -g # 全局安装 1.npm i ...