微服务架构和 IBM Bluemix
微服务是一种架构风格,通过 API 通讯,提供颗粒度小的服务群,近几年获得了越来越多的关注。有人认为微服务架构仅仅是
SOA 架构的重新包装,尽管存在着一些争论,但这种架构风格却对复杂应用和敏捷部署领域提供了巨大的帮助。
IBM Bluemix 是 PaaS 云平台,在体系架构方面,采用“星型”结构,奉行“拿来主义”,围绕着应用程序,绑定多个服务。在应用管理方面,Bluemix
能够很方便的构建和部署应用程序。
对于一个复杂应用的架构设计,架构师需要考虑的因素很多,除了功能和性能,还会顾及需求的变化对开发带来的影响,以及开发和部署应用的难易程度等等因素。在
Bluemix 中构建微服务架构应用是一种较好的实践。在本文的以下部分,我将结合一个案例,对该方案做一些介绍和分析。
案例系统架构概况
假设我们需要设计一个车联网项目的后台服务平台架构,该服务平台会收集车、车主、车状态(燃油量、里程、实时坐标等等)、停车场等信息,然后将这些信息提供给多种前端应用,比如,车实时信息查询、停车、停车监管、车险、租车等应用,以及将来拓展的其它应用。显然,需求的不确定性很大,系统的可拓展性要求高。
如果我们采用传统的单体式应用架构设计,那么,随着需求的改变和增加,该应用将不断变得庞大,程序更新带来的影响和风险也会变得越来越不可控制,噩梦从一开始就埋下了。
如果我们采用微服务架构会怎样呢?将车、车主、车状态、停车场等实体做成不同的应用,每个应用内,把实体的 CRUD
等操作封装成 APIs,然后根据不同的应用场景,将所需要的 APIs 组织成一个个不同的计划来对应。这就象“活字印刷术”,APIs
就是一个个的“活字”,可以被放到不同的字盘(计划)中任意“排版”。开发者将只需要重点关注 APIs 的设计,实现和更新。当有新的实体需要时,新增加一个应用,以及增加新的
CRUD 等操作 APIs。当有需求变化时,只需修改相应的 APIs。APIs 之间还可以相互调用,只要保证接口保持不变,无需关注其内部实现的更新。如此可见,这种方法能够大大降低需求变化给系统开发带来的影响。下图是结合
Bluemix 平台,以停车和停车监督两个应用为例,设计的系统架构概况图。
图 1. 示例系统架构图
数据采集,考虑到性能问题,数据采集会使用实时处理大数据流的方式将车实时状态信息写入到 Cloudant
数据库中,这与本文关系不大,不再详细讨论。
DevOps 服务,是 Bluemix 平台提供的服务。比如,把车实时状态信息存储在 Cloudant
NoSQL 数据中, 把车和车主这些主数据存储在 SQL Database 数据库中,Scheduler
定时从第三方平台去更新停车场信息,Devops 编译和部署应用,等等。根据需要,将 Bluemix 服务绑定到
Bluemix 应用上,这将大大提高运维效率。
Bluemix 应用和 APIs,是以实体为单位构建的 Bluemix 应用群,每个 Bluemix 应用都根据需求,提供
CRUD 等 APIs,可以很方便的增加新的 Bluemix 应用和新的 APIs。
APIm Gateway,是 Bluemix 提供的 API Management
服务,它可以组建 APIs 群,部署不同的计划,提供给前端不同的应用场景,并且能够监管 APIs 的调用情况。
构建微服务应用
在此章节中,我将一步步指导读者如何在 Bluemix 上构建微服务应用。
创建 Bluemix 应用
注册 Bluemix 账户并登陆
首先,您要有一个 Bluemix 账号,如果您还没有,您可以免费注册。登陆后,在主界面(DASHBORAD),您会看到默认已经有一个个人账号的组织(ORG),然后我们可以创建不同的空间,如开发空间,测试空间和产品空间来分隔不同环境中的应用。以下图为例,创建
dev 空间。
图 2. 首次登陆仪表板
创建 Bluemix 应用
接下来,我们就可以创建应用了。选择 Create App,新建一个 Cloud Foundry 应用。Bluemix
上托管的所有应用都是 Cloud Foundry Pass 云平台应用。我们选择 Web 应用来建立后台微服务应用,同时将以
Liberty for Java 作为运行环境。
图 3. 运行环境选择
以案例中的停车场 Bluemix 应用为例,新建应用并命名为“ParkingMicroService”。
图 4. 新建 Bluemix 应用界面
回到主界面,我们可以看到停车场 Bluemix 应用已经被构建并自动启动以及部署
图 5. 创建应用后的仪表板
添加和绑定 Bluemix 服务
Bluemix 平台提供了一些常用的功能模块,也就是服务(Bluemix Service),可以在应用中直接使用。通常,应用可以通过添加和绑定这两种形式建立与
Bluemix 平台提供的服务之间的关系。
添加服务
如果所需要的服务并没有被添加,则首先需要添加服务。
图 6. 添加服务界面
在服务目录(catalog)中找到需要的服务(Cloudant)。可以通过搜索栏直接输入服务名称进行查找。
图 7. 通过名称搜索 Bluemix 服务界面
然后点击该服务图标选择目标添加空间(space),应用名称和云空间大小。这样就完成了添加服务和绑定该服务到应用的操作。
图 8. Bluemix 服务创建界面
以同样的方式添加并绑定其他服务到应用上
图 9. 添加 Bluemix 服务后的应用详情界面
绑定服务
服务添加后,如果不同应用需要共享同一个服务,比如,访问同一个数据库,则需要选择绑定服务:首先在主界面(DASHBOARD)上单击需要绑定服务的应用,进入概况(Overview)界面,选择绑定服务,然后将该应用需要的服务勾选上就完成了服务的绑定。
比如,我们在新建完 ParkingMicroService 应用后,添加了一个 Monitoring and
Analytics 服务,当 VehicleMicroService 应用也需要使用相同服务时,并不是通过“Add
a service or api”再添加一个相同的服务, 而是直接选择“Bind a service or
api”绑定已有服务。通过这种方式,我们才可以实现应用之间相同服务的共享。
图 10. 绑定公用服务界面
当我们建好 ParkingMicroService 和 VehicleMicroService 这两个
Bluemix 工程后,回到主界面就可以看到已经建立的应用和已经添加的服务。 如图 11 所示,ParkingMicroService
应用和 VehicleMicroService 应用共享了 Cloudant 数据库服务。同时,ParkingMicroService
应用单独绑定了 DevOps 监控服务(Monitoring and Analytics),而 VehicleMicroService
应用单独绑定了 SQLDB 数据库服务。
图 11. 完成 Bluemix 应用创建和相应服务绑定后的仪表板
开发 Bluemix 应用
通常为了开发部署的便利,我们会将 Bluemix 应用的代码托管到 JazzHub 上。Bluemix
对 JazzHub 有着非常好的集成。
打开应用的 Overview 界面,我们可以看到应用名称的右侧有添加 ADD GIT 的按钮。
图 12. ParkingMicroService 的概览界面
点击后,Bluemix DevOps
会自动生成根据已配置信息生成的初始代码和一个托管在 JazzHub 上的代码托管仓库。
图 13. Bluemix 应用代码托管界面
图 14. 生成 GIT 代码托管地址
这样,在 JazzHub 上就建立了一个托管该应用代码的 Repository。JazzHub 协同软件工具能够和
Bluemix 平台非常棒的结合,集成敏捷计划、开发、构建和部署。相关的设置我们会在本文后面谈到。
建立好 Bluemix 应用对应的 JazzHub 代码托管库后, 点开自动生成的 GIT URL,可以看到
。默认只有 master 一个分支。同时,DevOps 已经帮用户做了第一次代码提交,也就是 Initial
Commit。
图 15. JazzHub 工程界面
如图所示,自动生成的 code starter 文件目录包括:
1.src:用于存储 java 源文件
2.target:编译后的目标文件及生成的可部署 war 包
3.license.txt:版本号信息与版权信息
4.manifest.yml:Bluemix 应用清单文件。这个文件可以看做是 cf push 命令的补充。当
cf push 命令需要设置多个可选参数,如分配的内存大小,部署后是否启动等,这个命令就会变得很长。为了使
cf push 命令更加简洁,我们使用这个应用清单文件来存储这些可选参数。当 cf push 命令被执行的时候,会自动加载这个清单文件,生成完整的命令行。manifest.yml
文件涵盖了 Bluemix 应用的部署信息。manifest.yml 文件可以看做是 Cloud Foundry
部署命令的脚本,它帮助解决了 Cloud Foundry 部署命令冗长的问题。
清单 1.manifest.yml 示例代码
applications: - path: target/JavaHelloWorldApp.war memory: 512M instances: 1 domain: mybluemix.net name: ParkingMicroService host: parkingmicroservice disk_quota: 1024M services: - Cloudant NoSQL DB-i3 - Monitoring and Analytics-qp |
5.pom.xml:maven 工程的依赖管理文件,包括依赖的库文件管理,插件管理等。
以下,我们以 VehicleMicroService 工程为例,进行代码部分的开发。
如果习惯于使用 Eclipse,可以通过 Egit 插件从 JazzHub 导入工程进行配置 (File->Import->clone
from Git),实现使用 Eclipse 开发部署 Bluemix 应用。
图 16. 通过 Egit 下载的模板工程文件结构如图所示
从已下载的模板工程可以看出,所有模板工程都被默认命名为“JavaHelloWorldApp”。为了不与其他工程重名,我们需要先对其重命名。需要修改如下几个文件:
Step1: 对整个工程进行重命名
图 17. 对本地工程重命名
Step 2: 修改 pom.xml 文件,将 artifactId 修改成工程名以保证项目名称与项目唯一标示符一致。同时,修改
maven 打出的 war 包名称,以保持一致性。
清单 2.pom.xml 文件部分代码片段
<groupId>net.wasdev.wlp.sample</groupId> <artifactId>VehicleMicroService</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> <warName>VehicleMicroService</warName> </configuration> </plugin> <plugin> <groupId>net.wasdev.wlp.maven.plugins</groupId> <artifactId>liberty-maven-plugin</artifactId> <version>1.1</version> </plugin> </plugins> </pluginManagement> </build> |
Step 3: 修改 manifest 文件,将打包路径(path)里面的 war 包名称改成与项目名称一致。
清单 3.修改打包路径后的 manifest.yml 文件代码
applications: - path: target/VehicleMicroService.war memory: 512M instances: 1 domain: mybluemix.net name: VehicleMicroService host: vehiclemicroservice disk_quota: 1024M services: - SQL Database-d9 |
Step 4: 修改 server.xml 文件,将应用名称改成与项目名称一致,并将应用的文档基本目录修改成当前目录。
清单 4.server.xml 文件代码片段
<server> <featureManager> <feature>servlet-3.1</feature> </featureManager> <httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080"> <tcpOptions soReuseAddr="true"/> </httpEndpoint> <application name="VehicleMicroService" context-root="/" location="${appLocation}" type="war"/> </server> |
Step 5: 重新 build 工程查看修改是否成功,并将上述修改提交到 jazz hub。
REST API
假设,停车场 Bluemix 应用(ParkingMicroService)需要提供如下 APIs:
/bays/eligibility/query
/bays/query
/bays/{id}/vehicles/query.
在汽车 Bluemix 应用 (VehicleMicroService) 中,设计了如下 APIs:
/vehicles
/vehicles/query
/vehicles/query/polygon
其中 ParkingMicroService 应用的第三个接口/bays/{id}/vehicles/query
调用了 VehicleMicroService 中的/vehicles/query/polygon 接口。
通过这种互相调用,减少了代码重复,降低系统耦合性。
利用 JAXRS 技术开发 REST API
Step1. 为了利用 liberty 提供的 JAXRS 特性进行 API 开发,我们需要修改 server.xml
以添加新的特性。
清单 5.加入 JAXRS 特性的 server.xml 代码片段
<featureManager> <feature>servlet-3.1</feature> <feature>jaxrs-2.0</feature> </featureManager> |
Step 2. 通过在 pom.xml 文件中添加对 JAXRS 相关库的依赖实现
清单 6.pom.xml 中 JAXRS 依赖相关代码片段
<dependency> <groupId>javax.ws.rs</groupId> <artifactId>javax.ws.rs-api</artifactId> <version>2.0.1</version> </dependency> |
Step 3. 在 webapp 文件夹中添加 WEB-INF 文件夹,并在该文件夹中添加 web.xml
文件。用 servlet 元素分配名称,使用 servlet-mapping 元素将定制的 URL 与刚分配的名称相关联。在<servlet-mapping>元素中,更改<url-pattern>以更改默认网址资源路径。
清单 7.web.xml 文件代码
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>Vehicle Micro Service</display-name> <servlet> <servlet-name>javax.ws.rs.core.Application</servlet-name> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>javax.ws.rs.core.Application</servlet-name> <url-pattern>/api/*</url-pattern> </servlet-mapping> </web-app> |
停车场 Bluemix 应用相关 APIs 实现代码
在 ParkingMicroService 的 src 文件夹中新建 Parking.java 文件,用来实现相关
APIs 接口。
清单 8.Parking.java 文件代码
import javax.ws.rs.POST; import javax.ws.rs.Path;
@Path("/bays")
public class Parking {
@POST
@Path("/eligibility/query")
public String findEligibility() {
return "eligibility info goes here.";
}
@POST
@Path("/{bay_id}/vehicles/query")
public String findVehiclesInBay(){
return "vehicles info in bay goes here";
}
@POST
@Path("/query")
public String findBays(){
return "parking bays info goes here";
}
} |
汽车微服务相关 API 实现代码
在 VehicleMicroService 的 src 文件夹中新建 Vehicle.java 文件,用来实现相关
APIs 接口。
清单 9.Vehicle.java 代码
import
javax.ws.rs.POST;
import javax.ws.rs.Path;
@Path("/vehicles")
public class vehicle {
@POST
public String getAllVehicles(){
return "get the list of all vehicles";
}
@POST
@Path("/query")
public String getAllVehiclesInfo() {
return "get detailed info of all vehicles";
}
@POST
@Path("/query/polygon")
public String getVehicleGeoInfo() {
return "get geo info of a vehicle";
}
} |
部署 Bluemix 应用
Bluemix 的 DevOps 服务提供了建立和部署的特性,这个特性又被称为部署管道 (Pipeline)。顾名思义,这个部署管道可以为开发者提供持续集成部署相关服务。
以汽车应用为例:
在没有进行任何代码提交之前,进入 VehicleMicroService 工程的 JazzHub 页面,可以看到右上角的
BUILD&DEPLOY 按钮。点击进入后,如图所示:
图 18. DevOps Stages 界面
可以看出,当建好工程后,DevOps 会默认建好两个 Stages。当代码更新后,Bluemix Devops
会自动帮助完成构建和部署工作。如果不想让它自动部署,可以通过更改 Stage Trigger 中的选项,实现手动部署。
图 19. 通过 Bluemix Devops 部署后的界面
通过使用 REST 工具测试部署好的 Bluemix 应用:
图 20. 使用 postman 测试已部署到 Bluemix 的 VehicleMicroService
工程
在 Bluemix 中构建微服务架构应用的思考
根据我们的实践体会,在 Bluemix 中构建微服务架构应用的优势很明显,主要体现在如下几点:
1. 分解复杂地单体应用为可拓展的应用群,使每一个应用易于理解,开发和维护
我们可以采用面向对象的思想来理解需求,将需求中相关的事务(对象)规划为一个个的 Bluemix 应用群,再把需求(动作)归类到不同应用中的微服务。每个
Bluemix 应用由不同的开发者或组来负责,任务分解明确,降低耦合性。Bluemix 平台提供的“星形”平台架构思想也能够很好的服务于微服务架构应用,实现快速开发。当需求有变化时,开发人员只需要修改相应的应用,并且只需要更新该应用的部署,对其他应用没有影响,对整体影响最小,也便于维护。
2.Bluemix 提供的 Devops 工具,将多个应用部署问题变得简单
单体应用分解后,变成多个应用,不可避免会给部署等工作带来额外的工作量,Devops 可以轻松地解决该问题,使得源代码的管理、部署、更新计划实现自动化或者半自动化。
3.API Management,将杂乱的 APIs 清晰地对应到不同的应用场景
如此多的 Bluemix 应用群和微服务,一定会让前端开发人员“眼花缭乱”,“该用、不该用、可用、不可用”的选择不是开发人员希望看到的,API
Management 能很好的解决这个问题。不仅如此,API Management 还能够做到关于认证、安全、统计等功能。
4. 微服务应用可以根据每个实体的特点,独立开发,互不干扰
微服务架构的耦合性低,应用之前的唯一关联就是调用 APIs,所以在每个应用的内部实现方法互不干扰。比如,在选择数据类别时,事务性比较强的实体,可以选择关系型数据库,数据结构不固定或者容易变化的实体,可以选择NoSQL。再比如,在选择绑定的服务时,也是按需而为,只绑定应用内所需的外部服务。
任何事物都是有两面性的,微服务应用也有不足之处:
1. 内部调用会增加通讯时间
内部使用其他微服务,也是通过调用 API 的方式,有调用就一定会增加调用的通讯时间,相比单体应用,在不考虑服务器能力的情况下,理论上会降低效率。
2. 部署相对单体式应用会变得复杂
从单一应用部署变成应用群部署,肯定会带来一些额外的工作量,也包括管理上的工作量。
结束语
在前后端分离的应用中,比如移动应用项目的开发,尤其是后端是一个服务平台,支撑前端多个应用的场景,构建微服务架构应用是一种很好的实践,在
Bluemix 中构建微服务架构应用是一种如虎添翼的实践。正确的使用 Bluemix,借助 Bluemix
提供的丰富的可用服务,可以快速地实现模块化微服务的构建和部署。我们相信借助于类似 Bluemix 这种
PasS 平台构建微服务解决复杂问题会成为未来趋势。
|