- 微服务架构教程
- 微服务架构 - 首页
- 简介
- 微服务架构 - 扩展
- 微服务架构 - 蓝图
- 不同元素
- 组合模式
- SOA 实践
- MSA 实践
微服务架构 - 快速指南
微服务架构 - 简介
微服务是一种基于服务的应用程序开发方法。在这种方法中,大型应用程序将被分解成最小的独立服务单元。微服务是通过将整个应用程序划分为相互连接的服务集合来实现面向服务架构 (SOA) 的过程,其中每个服务只满足一个业务需求。
走向微服务的概念
在面向服务架构中,整个软件包将被细分为小型、相互连接的业务单元。这些小型业务单元中的每一个都将使用不同的协议相互通信,以向客户端提供成功的业务。现在问题是,微服务架构 (MSA) 与 SOA 有何不同?简单来说,SOA 是一种设计模式,而微服务是实现 SOA 的一种实现方法,或者我们可以说微服务是 SOA 的一种类型。
以下是在开发面向微服务的应用程序时需要牢记的一些规则。
独立性 - 每个微服务都应该可以独立部署。
耦合性 - 所有微服务都应该彼此松散耦合,以便一个服务的更改不会影响其他服务。
业务目标 - 整个应用程序的每个服务单元都应该是最小的,并且能够实现一个特定的业务目标。
让我们以在线购物门户为例来深入了解微服务。现在,让我们将整个电子商务门户网站分解成小型业务单元,例如用户管理、订单管理、结账、支付管理、配送管理等。一个成功的订单需要在特定时间范围内通过所有这些模块。以下是与一个电子商务系统相关的不同业务单元的综合图像。
这些业务模块中的每一个都应该有自己的业务逻辑和利益相关者。它们为了某些特定需求与其他第三方供应商软件通信,也彼此通信。例如,订单管理可能会与用户管理通信以获取用户信息。
现在,假设您正在运行一个包含前面提到的所有这些业务单元的在线购物门户,您确实需要一个包含不同层(例如前端、后端、数据库等)的企业级应用程序。如果您的应用程序没有扩展并且完全在一个 WAR 文件中开发,那么它将被称为典型的单体应用程序。根据 IBM 的说法,一个典型的单体应用程序在内部应该具有以下模块结构,其中只有一个端点或应用程序负责处理所有用户请求。
在上图中,您可以看到不同的模块,例如用于存储不同用户和业务数据的数据库。在前端,我们有不同的设备,我们通常在这些设备上呈现用户或业务数据以供使用。在中间,我们有一个包,它可以是可部署的 EAR 或 WAR 文件,该文件接收来自用户端的请求,在资源的帮助下处理请求,并将其呈现回用户。只要业务想要对上述示例进行任何更改,一切都会很好。
考虑以下您必须根据业务需求更改应用程序的场景。
业务部门需要对“搜索”模块进行一些更改。然后,您需要更改整个搜索过程并重新部署您的应用程序。在这种情况下,您正在重新部署其他单元,而没有任何更改。
现在,您的业务部门再次需要对“结账”模块进行一些更改以包含“钱包”选项。您现在必须更改您的“结账”模块并将其重新部署到服务器中。请注意,您正在重新部署软件包的不同模块,而我们对此没有任何更改。这就是面向服务的架构,更具体地说,是微服务架构的概念。我们可以以这样一种方式开发我们的单体应用程序,使软件的每个模块都充当一个独立的单元,能够独立处理单个业务任务。
请考虑以下示例。
在上述架构中,我们没有创建任何具有紧凑端到端服务的 EAR 文件。相反,我们通过将软件的不同部分公开为服务来划分它们。软件的任何部分都可以通过使用各自的服务轻松地相互通信。这就是微服务在现代 Web 应用程序中发挥重要作用的方式。
让我们按照微服务的方式比较我们的购物车示例。我们可以将我们的购物车分解成不同的模块,例如“搜索”、“筛选”、“结账”、“购物车”、“推荐”等。如果我们想构建一个购物车门户,那么我们必须以这样一种方式构建上述模块,使它们能够相互连接,为您提供 24x7 的良好购物体验。
优点和缺点
以下是使用微服务而不是使用单体应用程序的一些优点。
优点
体积小 - 微服务是 SOA 设计模式的实现。建议您尽可能保持服务的小巧。基本上,服务不应该执行多个业务任务,因此它显然比任何其他单体应用程序都更小且易于维护。
专注 - 如前所述,每个微服务都设计用于执行一项业务任务。在设计微服务时,架构师应该关注服务的焦点,即其可交付成果。根据定义,一个微服务应该具有全栈特性,并且应该致力于交付唯一的业务属性。
自治 - 每个微服务都应该是整个应用程序的自治业务单元。因此,应用程序变得更加松散耦合,这有助于降低维护成本。
技术异构性 - 微服务支持不同的技术在同一个业务单元中相互通信,这有助于开发人员在正确的地方使用正确的技术。通过实现异构系统,人们可以获得最大的安全性、速度和可扩展的系统。
弹性 - 弹性是隔离软件单元的属性。微服务在构建方法中遵循高水平的弹性,因此当一个单元发生故障时,它不会影响整个业务。弹性是另一个实现高度可扩展和松散耦合系统的属性。
易于部署 - 由于整个应用程序被细分为小型单元,因此每个组件都应该具有全栈特性。与同类其他单体应用程序不同,所有这些都可以在任何环境中非常轻松地部署,并且时间复杂度更低。
以下是微服务架构的一些缺点。
缺点
分布式系统 - 由于技术异构性,将使用不同的技术来开发微服务的不同部分。需要大量熟练的专业人员来支持这个大型异构分布式软件。因此,分布式和异构性成为使用微服务的主要缺点之一。
成本 - 微服务成本很高,因为您必须为不同的业务任务维护不同的服务器空间。
企业就绪性 - 微服务架构可以被认为是不同技术的集合,因为技术日新月异。因此,与传统的软件开发模型相比,使微服务应用程序具备企业就绪性非常困难。
微服务与 SOA
下表列出了 SOA 和微服务的某些特性,突出了在 SOA 上使用微服务的重要性。
组件 | SOA | 微服务 |
---|---|---|
设计模式 | SOA 是一种用于计算机软件的设计范例,其中软件组件以服务的形式公开给外部世界以供使用。 | 微服务是 SOA 的一部分。它是 SOA 的一种专门实现。 |
依赖性 | 业务单元相互依赖。 | 所有业务单元彼此独立。 |
大小 | 软件大小大于传统软件。 | 软件体积小。 |
技术 | 技术栈少于微服务。 | 微服务本质上是异构的,因为使用精确的技术来执行特定任务。微服务可以被认为是许多技术的集合。 |
自治和专注 | SOA 应用程序构建用于执行多个业务任务。 | 微服务应用程序构建用于执行单个业务任务。 |
性质 | 单体性质。 | 全栈性质。 |
部署 | 部署耗时。 | 部署非常容易。因此,它将耗时更少。 |
成本效益 | 更具成本效益。 | 成本效益较低。 |
可扩展性 | 与微服务相比较低。 | 完全扩展。 |
示例 | 让我们考虑一个在线出租车预订应用程序。如果我们想使用 SOA 构建该应用程序,那么它的软件单元将是 -
|
如果使用微服务架构构建相同的应用程序,那么它的 API 将是 -
|
微服务架构 - 扩展
扩展是将软件分解成不同单元的过程。扩展也以可扩展性的形式定义。可扩展性是实施应用程序更高级功能的潜力。它有助于提高应用程序的安全、持久性和可维护性。行业中遵循三种类型的扩展程序。以下是不同的扩展方法以及相应的现实生活示例。
X 轴扩展
X轴缩放也称为水平缩放。在此过程中,整个应用程序被细分为不同的水平部分。通常,任何 Web 服务器应用程序都可以具有这种类型的缩放。考虑一个遵循水平缩放的普通 MVC 架构,如下图所示。
例如,我们可以考虑任何 JSP servlet 应用程序。在此应用程序中,控制器控制每个请求,并在必要时通过与模型通信来生成视图。通常,单体应用程序遵循此缩放方法。X轴缩放本质上非常基础,并且耗时很少。在这种方法中,一个软件将根据其负责的不同任务进行缩放。例如,控制器负责控制传入和传出的请求,视图负责向浏览器中的用户展示业务功能,而模型负责存储我们的数据,并充当数据库。
Y轴缩放
Y轴缩放也称为垂直缩放,它包括任何资源级别的缩放。任何 DBaaS 或 Hadoop 系统都可以被认为是 Y轴缩放的。在这种类型的缩放中,用户的请求通过实现某些逻辑来重定向和限制。
让我们以 Facebook 为例。Facebook 需要每秒处理 179 万用户;因此,控制流量是 Facebook 网络工程师的一项重大责任。为了克服任何危险,他们遵循 Y轴缩放,其中包括同时运行多个具有相同应用程序的服务器。现在,为了控制这种巨大流量级别,Facebook 将来自一个区域的所有流量重定向到特定服务器,如图像所示。从架构语言的角度来看,这种基于区域的流量传输称为负载均衡。
这种将资源分解成小的独立业务单元的方法称为 Y轴缩放。
Z轴缩放
X轴和 Y轴缩放很容易理解。但是,一个应用程序也可以在业务级别进行缩放,这称为 Z轴缩放。以下是按业务单元的不同垂直方向扩展出租车服务应用程序的示例。
缩放的优势
成本 - 正确的软件缩放将降低维护成本。
性能 - 由于松耦合,正确缩放的软件的性能始终优于未缩放的软件。
负载分配 - 使用不同的技术,我们可以轻松地维护服务器负载。
重用 - 软件的可扩展性也提高了软件的可用性。
微服务架构 - 蓝图
微服务在内部实现了 SOA。从更广泛的意义上说,我们可以将其视为一个 SOA 应用程序的子集。
规则和工作流
以下是开发微服务时需要注意的原则。
高内聚性 - 所有业务模型都需要尽可能细化到最小的业务部分。每个服务都应该专注于执行一项业务任务。
独立性 - 所有服务都应该具有完整的功能,并且彼此独立。
以业务域为中心 - 软件将根据业务单元进行模块化,而不是基于层级。
自动化 - 测试部署将自动化。尽量减少人工干预。
可观察性 - 每个服务都将具有完整的功能,并且应该像企业应用程序一样独立部署和可观察。
团队管理
“两个披萨规则”是一种限制微服务开发团队参与者数量的规则。根据此规则,一个应用程序的团队成员数量应该很小,以便两个披萨可以喂饱他们。通常,人数不应超过 8 人。由于微服务具有完整的功能,因此团队也具有完整的功能。为了提高生产力,我们需要建立一个最多 8 名成员的团队,其中包含该服务所需的所有专业知识。
任务管理
任务在软件开发生命周期中扮演着重要的角色。开发大型应用程序可以分解成几个小的任务单元。假设我们需要开发一个像 Facebook 这样的应用程序。那么,“登录”功能可以被视为整个构建过程中的一个任务。每个任务的进度都需要在高技能专业人员的监督下得到妥善监控。敏捷是行业中遵循的著名流程结构,以保持良好的任务管理。
不同元素
到目前为止,我们已经了解了什么是微服务以及在现代 MVC 架构之上它有哪些基本需求。在本章中,我们将学习该架构的不同元素,这些元素对于服务同样重要。
服务的类别
从微服务这个名称来看,我们假设它将是一个可以通过 HTTP 协议使用的服务,但是我们需要知道使用这种架构可以构建哪些类型的服务。以下是可以使用微服务架构实现的服务列表。
平台即服务 [PaaS] - 在这种面向服务的架构中,平台作为一种工具提供,可以根据业务需求进行定制。PaaS 在移动应用程序开发中发挥着重要作用。PaaS 最好的例子是 Google App Engine,其中 Google 提供了不同的实用平台来构建您的应用程序。PaaS 最初是为了为开发人员提供内置的架构或基础设施而开发的。它以大大减少的时间显著降低了高级编程的复杂性。以下是 Google 提供的 PaaS 的快照。
软件即服务 [SaaS] - 软件即服务是一种软件许可业务,其中软件集中托管并以订阅方式获得许可。SaaS 主要可以通过浏览器访问,并且在许多业务垂直领域(如人力资源管理 (HRM)、企业资源规划 (ERP)、客户关系管理 (CRM) 等)中是一种非常常见的架构模式。以下屏幕截图显示了 Oracle 提供的不同 SaaS 的示例。
基础设施即服务 [IaaS] - 基础设施在 IT 行业中发挥着重要作用。使用云计算,一些组织将其虚拟基础设施作为服务提供。IaaS 有助于在软件开发中带来敏捷性、成本效益、安全性、性能、生产力等。Amazon EC2 和 Microsoft Azure 是 IaaS 的最大示例。下图描绘了 AWS 的一个示例,其中数据中心作为 IaaS 提供。
数据即服务 [DaaS] - 信息技术处理数据,一些行业领导者认为数据将成为社会新的支柱。DaaS 是一种服务类型,其中数据与企业集团共享以进行研究和分析。DaaS 为数据访问层带来了简单性、敏捷性和安全性。以下是一个 Oracle Data Cloud 的示例,您可以访问或许可它以满足您自己的业务需求。
后端即服务 [BaaS] - BaaS 也称为 MBaaS,即移动后端即服务。在这种类型的服务中,应用程序的后端将提供给业务部门以供其自身的业务冒险。所有推送通知、社交网络服务都属于此类服务。Facebook 和 Twitter 是知名的 BaaS 服务提供商的例子。
安全
在处理大量客户数据时,安全性起着重要作用。安全问题与市场上所有类型的服务相关联。无论您使用哪种云 - 私有、公共、混合等,都应在所有级别维护安全性。整个安全问题可以大致细分为以下几个部分 -
服务提供商面临的安全问题 - 这种类型的安全问题是服务提供商(如 Google、Amazon 等)面临的。为了确保安全保护,有必要对客户端进行背景调查,尤其是那些可以直接访问云核心部分的客户端。
消费者面临的安全问题 - 云具有成本效益,因此在各行各业中得到广泛使用。一些组织将用户详细信息存储在第三方数据中心,并在需要时提取数据。因此,必须维护安全级别,以确保一个客户的任何私人数据对任何其他用户不可见。
为了防止上述安全问题,以下是组织使用的一些防御机制。
威慑控制 - 了解您的潜在威胁以减少网络攻击。
预防控制 - 维护高级身份验证策略以访问您的云。
侦查控制 - 监控您的用户并检测任何潜在风险。
纠正控制 - 与不同的团队密切合作,修复在侦查控制阶段出现的任何问题。
组合模式
软件构成是指构建软件产品的方式。基本上,它涉及高级软件架构图,其中软件的不同模块将为了特定的业务目标而进行通信。在本章中,我们将学习组织中广泛使用的不同软件组合模式。在微服务中,我们将每个功能拆分为一个进程。这些服务中的每一个都将是独立的,并且具有完整的功能。
功能分解在构建微服务中发挥着重要作用。它为您的应用程序提供了敏捷性、灵活性和可扩展性。
聚合器模式
聚合器模式是在开发微服务时可以实现的最简单的 Web 模式。在此组合模式中,一个简单的 Web 模块将充当负载均衡器,这意味着它将根据需要调用不同的服务。下图显示了一个带有聚合器设计的简单微服务 Web 应用程序。如以下图像所示,“聚合器”负责依次调用不同的服务。如果我们需要对服务 A、B 和 C 的结果应用任何业务逻辑,那么我们可以在聚合器本身中实现业务逻辑。
聚合器可以再次作为另一个服务公开给外部世界,并在需要时被其他人使用。在开发聚合器模式 Web 服务时,我们需要记住我们的每个服务 A、B 和 C 都应该有自己的缓存层,并且应该具有完整的功能。
代理模式
代理微服务模式是聚合器模型的一个变体。在此模型中,我们将使用代理模块而不是聚合模块。代理服务可以单独调用不同的服务。
在代理模式中,我们可以通过提供一个虚拟代理层来构建一层额外的安全性。此层的作用类似于接口。
链式模式
顾名思义,这种组合模式将遵循链式结构。在这里,我们不会在客户端和服务层之间使用任何东西。相反,我们将允许客户端直接与服务通信,并且所有服务都将以这样一种方式链接起来,即一个服务的输出将成为下一个服务的输入。下图显示了一个典型的链式模式微服务。
此架构的一个主要缺点是,客户端将被阻塞,直到整个过程完成。因此,强烈建议将链的长度保持尽可能短。
分支微服务模式
分支微服务是聚合器模式和链式模式的扩展版本。在这种设计模式中,客户端可以直接与服务通信。此外,一个服务可以同时与多个服务通信。以下是分支微服务的示意图。
分支微服务模式允许开发人员动态配置服务调用。所有服务调用都将并发进行,这意味着服务 A 可以同时调用服务 B 和 C。
共享资源模式
共享资源模式实际上是前面提到的所有类型模式的集合。在这种模式下,客户端或负载均衡器将在必要时直接与每个服务通信。这是大多数组织广泛采用的最有效的设计模式。以下是共享资源设计模式的示意图。
微服务架构 - SOA 实战
在本章中,我们将开发一个基于 CRUD 的 SOA 架构应用程序。在后续章节中,我们将把此服务分解成微服务,并学习 SOA 和微服务架构之间的基本区别。
系统配置和设置
在本节中,我们将构建一个示例 CRUD 应用程序,每当我们调用我们的服务时,它将返回一个 JSON 对象作为响应。我们将使用 Jersey 框架来开发它。以下是设置本地系统环境的步骤。
开发 CRUD 应用程序
步骤 1 - 我们将使用 NetBeans 作为开发 IDE。请从 NetBeans 官方网站下载并安装最新版本 https://netbeans.org/downloads/。
步骤 2 - 打开 NetBeans IDE。转到“文件 -> 新建项目”。将弹出以下屏幕截图。选择“Maven”作为类别,选择“从 ArchType 创建项目”作为项目,然后点击下一步。
这将下载创建第一个 Maven 项目和 RESTful Web 服务所需的所有 jar 文件。
步骤 3 - 在上一步点击下一步按钮后,将出现以下屏幕截图。在这里,您必须指定 Maven 原型。
在搜索框中,搜索“Jersey-archType-Webapp(2.16)”并选中“显示旧版本”复选框。
步骤 4 - 选择后,您将被重定向到以下屏幕。从列表中选择首选的 jar 并点击下一步继续。
步骤 5 - 在此步骤中,您需要提供项目的名称及其 Group Id 以及包详细信息。提供所有这些信息后,点击完成继续。
步骤 6 - 您已完成工作区设置。项目目录将如下所示。
查看您的“依赖项”文件夹,您会发现 Maven 已自动下载了此项目所需的所有 jar 文件。
步骤 7 - 您的工作区已设置好,您可以开始编码了。继续并创建四个类和包,如下面的屏幕截图所示。您可以看到 MyResource.java 已经由 Maven 创建,因为 Maven 足够智能,可以检测到您将要构建自己的 Web 服务。
步骤 8 - 完成上述步骤后,我们将构造我们的 POJO 类 UserProfile.java,如下所示。
package com.tutorialspoint.userprofile.Model; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public class UserProfile { private long ProId; private String FName; private String LName; private String Add; public UserProfile(){} public UserProfile(long Proid, String Fname, String Lname,String Add) { this.ProId = Proid; this.FName = Fname; this.LName = Lname; this.Add = Add; } public long getProId() { return ProId; } public void setProId(long ProId) { this.ProId = ProId; } public String getFName() { return FName; } public void setFName(String FName) { this.FName = FName; } public String getLName() { return LName; } public void setLName(String LName) { this.LName = LName; } public String getAdd() { return Add; } public void setAdd(String Add) { this.Add = Add; } }
步骤 9 - 现在我们将创建我们的数据库类。由于这是学习资料的一部分,我们不会使用任何数据库作为我们的数据库。我们将使用内置的 Java 内存作为我们的临时内存。正如您在以下代码集中看到的,我们将使用 MAP 作为我们的数据库。我们执行的所有 Web 服务操作都将在此类中定义的 MAP 上进行。
package com.tutorialspoint.userprofile.DAO; import com.tutorialspoint.userprofile.Model.UserProfile; import java.util.HashMap; import java.util.Map; public class DatabaseClass { private static Map<Long,UserProfile> messages = new HashMap<Long,UserProfile>(); public static Map<Long,UserProfile> getUsers() { return messages; // Each time this method will return entire map as an instance of database } }
步骤 10 - 现在让我们构建我们的服务类。继续并将以下代码集复制粘贴到“ProfileService.java”类中。这是我们将声明所有要公开给外部世界的 Web 服务方法的类。我们需要创建一个数据库类的引用,以便我们的临时数据库可以在此类中访问。
package com.tutorialspoint.userprofile.service; import com.tutorialspoint.userprofile.DAO.DatabaseClass; import com.tutorialspoint.userprofile.Model.UserProfile; import java.util.ArrayList; import java.util.List; import java.util.Map; public class ProfileService { private Map<Long,UserProfile> Userprofiles = DatabaseClass.getUsers(); // Creating some predefine profile and populating the same in the map public ProfileService() { UserProfile m1 = new UserProfile(1L,"Tutorials1","Point1","TutorialsPoint.com"); UserProfile m2 = new UserProfile(2L,"Tutorials2","Point2","TutorialsPoint.com2"); UserProfile m3 = new UserProfile(3L,"Tutorials3","Point3","TutorialsPoint.com3"); UserProfile m4 = new UserProfile(4L,"Tutorials4","Point4","TutorialsPoint.com4"); Userprofiles.put(1L, m1); Userprofiles.put(2L, m2); Userprofiles.put(1L, m3); Userprofiles.put(2L, m4); } //Method to fetch all profile public List<UserProfile> getAllProfile() { List<UserProfile> list = new ArrayList<UserProfile>(Userprofiles.values()); return list; } // Method to fetch only one profile depending on the ID provided public UserProfile getProfile(long id) { return Userprofiles.get(id); } //Method to add profile public UserProfile addProfile(UserProfile UserProfile) { UserProfile.setProId(Userprofiles.size()+1); Userprofiles.put(UserProfile.getProId(), UserProfile); return UserProfile; } //method to update Profile public UserProfile UpdateProfile(UserProfile UserProfile) { if(UserProfile.getProId()<=0) { return null; } else { Userprofiles.put(UserProfile.getProId(), UserProfile); return UserProfile; } } //method to delete profile public void RemoveProfile(long Id) { Userprofiles.remove(Id); } }
步骤 11 - 在此步骤中,我们将创建我们的资源类,该类将与 URL 链接,并将调用相应的服务。
package com.tutorialspoint.userprofile.Resource; import com.tutorialspoint.userprofile.Model.UserProfile; import com.tutorialspoint.userprofile.service.ProfileService; import java.util.List; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("/Profile") @Consumes(MediaType.APPLICATION_XML) @Produces(MediaType.APPLICATION_XML) public class ProfileResource { ProfileService messageService = new ProfileService(); @GET public List<UserProfile> getProfile() { return messageService.getAllProfile(); } @GET @Path("/{ProID}") public UserProfile getProfile(@PathParam("ProID")long Id) { return messageService.getProfile(Id); } @POST public UserProfile addProfile(UserProfile profile) { return messageService.addProfile(profile); } @PUT @Path("/{proID}") public UserProfile UpdateProfile(@PathParam("proID")long Id,UserProfile UserProfile) { UserProfile.setProId(Id); return messageService.UpdateProfile(UserProfile); } @DELETE @Path("/{ProID}") public void deleteProfile(@PathParam("ProID")long Id) { messageService.RemoveProfile(Id); } }
步骤 12 - 清理构建项目并运行它。如果一切顺利,您应该在浏览器中获得以下输出,同时访问https://127.0.0.1:8080/UserProfile/webapi/Profile” URL。
您可以看到使用 XML 表示法填充了不同的条目。
可以通过使用正确的 Method URL 通过 Postman 测试不同的方法。
@GET 方法 - 以下屏幕截图演示了如何获取 get 请求的所需结果,该结果返回所有用户详细信息。
@POST - 以下请求可用于测试我们的 Post 方法。请注意 proId 如何自动生成。
@PUT - 此方法将更新条目。以下屏幕截图演示了 Jersey 如何从请求 URL 中获取 proId 并更新相同的用户配置文件回复。
同样,您可以检查 Web 服务中可用的其他方法。
在上一节中,我们开发了一个服务,该服务将公开 CRUD 功能。现在,每当我们尝试在我们的应用程序中实现此服务时,我们需要创建此应用程序的客户端并将其附加到我们的应用程序。在本章中,我们将学习如何使用微服务的概念构建此功能。以下是使用上述步骤构建的应用程序的示意图。
参与者应该是我们服务的入口点。在这种情况下,“ProfileResource.java”承担参与者的职责。此类将调用不同的方法来执行不同的操作,例如添加、更新和删除。
CRUD 应用程序的分解
根据微服务的主要原则,我们需要为每个模块只有一个业务任务,因此一个参与者不应该负责所有四个 CRUD 功能。考虑以下示例,我们引入了一些新角色,这样您就可以清楚地了解微服务是 SOA 的架构表示。
“主要用户”是与“应用程序控制器”通信以满足其需求的用户。“应用程序控制器”只是根据最终用户的请求调用不同的“资源管理器”。“资源管理器”执行需要完成的工作。让我们快速了解应用程序不同单元的不同角色。
最终用户/主要用户 - 请求某些资源到应用程序控制器。
应用程序 - 接收请求并将请求转发到特定的资源管理器。
资源管理器 - 执行更新、删除和添加用户的实际工作。
请查看一个类的总职责是如何分布在不同的其他类中的。
微服务架构 - MSA 实战
在本章中,我们将构建一个微服务应用程序,该应用程序将使用不同的可用服务。我们都知道,微服务不是构建应用程序的经济有效的方式,因为我们构建的每个服务本质上都是全栈的。在本地环境中构建微服务需要高端的系统配置,因为您需要拥有四个服务器实例以保持运行,以便可以在某个时间点使用它。为了构建我们的第一个微服务,我们将使用一些可用的 SOA 端点,并在我们的应用程序中使用它们。
系统配置和设置
在进一步进入构建阶段之前,请相应地准备您的系统。您将需要一些公共 Web 服务。您可以轻松地通过 Google 搜索。如果您想使用 SOAP Web 服务,那么您将获得一个 WSDL 文件,并且您需要从中使用特定的 Web 服务。对于 REST 服务,您只需要一个链接来使用它。在本例中,您将在一个应用程序中使用三个不同的 Web 服务“SOAP”、“REST”和“自定义”。
应用程序架构
您将使用微服务实施计划创建一个 Java 应用程序。您将创建一个自定义服务,此服务的输出将作为其他服务的输入。
以下是开发微服务应用程序需要遵循的步骤。
步骤 1:创建 SOAP 服务的客户端 - 有许多免费的 Web API 可用于学习 Web 服务。出于本教程的目的,使用“http://www.webservicex.net/”的 GeoIP 服务。WSDL 文件在其网站上的以下链接中提供“webservicex.net. 要从此 WSDL 文件生成客户端,您需要做的就是在终端中运行以下命令。
wsimport http://www.webservicex.net/geoipservice.asmx?WSDL
此命令将在一个名为“SEI”的文件夹下生成所有必需的客户端文件,该文件夹以服务端点接口命名。
步骤 2:创建您的自定义 Web 服务 - 遵循本教程前面阶段中提到的相同过程,并构建一个名为“CustomRest”的基于 Maven 的 REST api。完成后,您将找到一个名为“MyResource.java”的类。继续并使用以下代码更新此类。
package com.tutorialspoint.customrest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; @Path("myresource") public class MyResource { @GET @Produces(MediaType.TEXT_PLAIN) public String getIt() { return "IND|INDIA|27.7.65.215"; } }
一切完成后,继续并在服务器上运行此应用程序。您应该在浏览器中获得以下输出。
这是 Web 服务器,它在被调用时返回一个字符串对象。这是提供可由其他应用程序使用以生成记录的输入的服务。
步骤 3:配置另一个 Rest API - 在此步骤中,使用位于services.groupkt.com. 的另一个 Web 服务。调用时,这将返回一个 JSON 对象。
步骤 4:创建 JAVA 应用程序 - 通过选择“新建项目”->“JAVA 项目”并点击完成创建一个普通的 Java 应用程序,如下面的屏幕截图所示。
步骤 5:添加 SOAP 客户端 - 在步骤 1 中,您已为 SOAP Web 服务创建了客户端文件。继续并将这些客户端文件添加到您当前的项目中。成功添加客户端文件后,您的应用程序目录将如下所示。
步骤 6:创建您的主应用程序 - 创建您的主类,您将在其中使用所有这三个 Web 服务。右键单击源项目并创建一个名为“MicroServiceInAction.java”的新类。接下来的任务是从这里调用不同的 Web 服务。
步骤 7:调用您的自定义 Web 服务 - 为此,请继续并将以下代码集添加到实现调用您自己的服务。
try { url = new URL("https://127.0.0.1:8080/CustomRest/webapi/myresource"); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); if (conn.getResponseCode() != 200) { throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode()); } BufferedReader br = new BufferedReader(new InputStreamReader( (conn.getInputStream()))); while ((output = br.readLine()) != null) { inputToOtherService = output; } conn.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
步骤 8:使用 SOAP 服务 - 您已生成客户端文件,但您不知道在整个包中应该调用哪个方法?为此,您需要再次参考用于生成客户端文件的 WSDL。每个 WSDL 文件都应该有一个“wsdl:service”标签,搜索此标签。它应该是该 Web 服务的入口点。以下是此应用程序的服务端点。
现在您需要在您的应用程序中实现此服务。以下是您需要实现 SOAP Web 服务的 Java 代码集。
GeoIPService newGeoIPService = new GeoIPService(); GeoIPServiceSoap newGeoIPServiceSoap = newGeoIPService.getGeoIPServiceSoap(); GeoIP newGeoIP = newGeoIPServiceSoap.getGeoIP(Ipaddress); // Ipaddress is output of our own web service. System.out.println("Country Name from SOAP Webserivce ---"+newGeoIP.getCountryName());
步骤 9:使用 REST Web 服务 - 到目前为止,已经使用了两个服务。在此步骤中,将借助您的自定义 Web 服务使用具有自定义 URL 的另一个 REST Web 服务。使用以下代码集来执行此操作。
String url1="http://services.groupkt.com/country/get/iso3code/";//customizing the Url url1 = url1.concat(countryCode); try { URL url = new URL(url1); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); if (conn.getResponseCode() != 200) { throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode()); } BufferedReader br = new BufferedReader(new InputStreamReader( (conn.getInputStream()))); while ((output = br.readLine()) != null) { System.out.println(output); } conn.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
步骤 10:使用所有服务 - 假设您的“CustomRest” Web 服务正在运行并且您已连接到互联网,如果一切成功完成,则以下应该是您的合并主类。
package microserviceinaction; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.StringTokenizer; import net.webservicex.GeoIP; import net.webservicex.GeoIPService; import net.webservicex.GeoIPServiceSoap; public class MicroServiceInAction { static URL url; static HttpURLConnection conn; static String output; static String inputToOtherService; static String countryCode; static String ipAddress; static String CountryName; public static void main(String[] args) { //consuming of your own web service try { url = new URL("https://127.0.0.1:8080/CustomRest/webapi/myresource"); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); if (conn.getResponseCode() != 200) { throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode()); } BufferedReader br = new BufferedReader(new InputStreamReader( (conn.getInputStream()))); while ((output = br.readLine()) != null) { inputToOtherService = output; } conn.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //Fetching IP address from the String and other information StringTokenizer st = new StringTokenizer(inputToOtherService); countryCode = st.nextToken("|"); CountryName = st.nextToken("|"); ipAddress = st.nextToken("|"); // Call to SOAP web service with output of your web service--- // getting the location of our given IP address String Ipaddress = ipAddress; GeoIPService newGeoIPService = new GeoIPService(); GeoIPServiceSoap newGeoIPServiceSoap = newGeoIPService.getGeoIPServiceSoap(); GeoIP newGeoIP = newGeoIPServiceSoap.getGeoIP(Ipaddress); System.out.println("Country Name from SOAP Webservice ---"+newGeoIP.getCountryName()); // Call to REST API --to get all the details of our country String url1 = "http://services.groupkt.com/country/get/iso3code/"; //customizing the Url url1 = url1.concat(countryCode); try { URL url = new URL(url1); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); if (conn.getResponseCode() != 200) { throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode()); } BufferedReader br = new BufferedReader(new InputStreamReader( (conn.getInputStream()))); while ((output = br.readLine()) != null) { System.out.println(output); } conn.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
运行此文件后,您将在控制台中看到以下输出。您已成功开发了您的第一个微服务应用程序。