写点什么

通过容器编排和服务网格来改进 Java 微服务的可测性

  • 2018-08-09
  • 本文字数:4774 字

    阅读完需:约 16 分钟

关键要点

  • 在企业测试中,测试软件的方式应该与软件在生产环境中运行的方式相同,以便确保软件能够按预期的方式运行。
  • 常见的挑战是微服务应用程序直接或间接依赖需要在测试场景中编排的其他服务。
  • 本文展示了容器编排如何在服务实例之上提供抽象,并使用模拟实例来替代真实实例。
  • 此外,服务网格让我们能够重新路由流量,并通过注入错误响应或延迟来验证服务的弹性。
  • 本文包含了一个示例代码,代码来自一个基于 Java 的咖啡店应用程序,该应用程序将被部署到 Kubernetes 和 Istio 上,并对其进行测试。

在企业测试中,测试软件的方式应该与软件在生产环境中运行的方式相同,以便确保软件能够按预期的方式运行。常见的挑战是微服务应用程序直接或间接依赖需要在测试场景中编排的其他服务。

本文展示了容器编排如何在服务实例之上提供抽象,并使用桩实例来替代真实实例。此外,服务网格让我们能够重新路由流量,并通过注入错误响应或延迟来验证服务的弹性。

我们将使用一个咖啡店示例应用程序,这个应用程序被部署在一个容器和服务网格集群中。我们选择Kubernetes 和Istio 作为实例运行环境。

测试场景

假设我们想要在不考虑其他外部服务的情况下测试应用程序的行为。应用程序的运行方式和配置方式应该与生产环境相同,以便确保以后它在生产环境中的行为是一致的。在测试中,我们将使用定义好的通信接口连接应用程序。

但是,外部服务不应成为测试场景的一部分。通常,在测试时我们应该关注被测试的对象,并忽略掉其他对象。因此,我们使用模拟服务器来替代外部服务。



容器编排

使用模拟服务器而不是真实实例与以与生产环境相同的方式运行微服务的想法相矛盾,因为到了生产环境配置会发生改变。但是,如果我们的应用程序部署到容器编排集群(例如 Kubernetes),就可以将抽象的服务名称用作配置,并让集群自己去解析后端服务实例。

以下示例是一个网关类,它是咖啡店应用程序的一部分,连接到端口 8080 上的 coffee-processor。

复制代码
public class OrderProcessor {
    // definitions omitted ...
    @PostConstruct
    private void initClient() {
        final Client client = ClientBuilder.newClient();
        target = client.target("http://coffee-processor:8080/processes");
    }
   @Transactional(Transactional.TxType.REQUIRES_NEW)
    public void processOrder(Order order) {
        OrderStatus status = retrieveOrderStatus(order);
        order.setStatus(status);
        entityManager.merge(order);
    }
    // ...
    private JsonObject sendRequest(final JsonObject requestBody) {
        Response response = target.request()
               .buildPost(Entity.json(requestBody))
                .invoke();
        // ...
        return response.readEntity(JsonObject.class);
    }
    // definitions omitted ...
}

主机名通过 Kubernetes 群集 DNS 解析,将流量引导到其中一个正在运行的处理器实例。然而,coffee-processor 的实例将成为一个模拟服务器,在我们的示例中使用了 WireMock 。这种替换对我们的应用来说是透明的。

在测试场景中,不仅会连接到应用程序来调用业务逻辑,还会与模拟服务器发生通信,在单独的管理界面上控制响应行为,并验证应用程序是否以正确的方式调用模拟服务器。这与类级别的单元测试类似,通常使用 JUnit 和 Mockito 实现。



外部服务

上述的设置可以让我们模拟和控制在容器编排集群内运行的服务。那么那些在集群之外的外部服务该怎么办呢?

通常,我们可以创建一个不带有选择器的Kubernetes 服务,让它指向一个外部IP,并重写我们的应用程序,让它始终使用由群集解析的服务名。这样一来,我们定义了一个单一的点,服务将被路由到这个点上。

以下的代码片段显示了一个外部 Kubernetes 服务和端点定义,它将 coffee-shop-db 路由到外部 IP 地址 1.2.3.4:

复制代码
kind: Service
apiVersion: v1
metadata:
  name: coffee-shop-db
spec:
  ports:
  - protocol: TCP
    port: 5432
---
kind: Endpoints
apiVersion: v1
metadata:
  name: coffee-shop-db
subsets:
  - addresses:
      - ip: 1.2.3.4
    ports:
      - port: 5432

在不同的环境中,服务可能会被路由到不同的数据库实例。

服务网格

服务网格能够帮助我们处理微服务间的通信问题。目前,Istio 是最常用的服务网格技术之一。它增加了与应用程序容器共存的边车代理容器,可以解决微服务间的通信问题,并且还可以用来操纵或减慢连接,以便进行弹性测试。

在端到端测试中,我们可以引入错误或缓慢的响应来验证应用程序是否能够正确处理这些问题场景。

以下的代码片段显示了一个 Istio 虚拟服务的定义,其中到 coffee-processor 的路由有 50% 的延迟为 3 秒,10%的响应是失败的。

复制代码
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: coffee-processor
spec:
  hosts:
  - coffee-processor
  http:
  - route:
    - destination:
        host: coffee-processor
        subset: v1
    fault:
      delay:
        fixedDelay: 3s
        percent: 50
      abort:
        httpStatus: 500
        percent: 10
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: coffee-processor
spec:
  host: coffee-processor
  subsets:
  - name: v1
    labels:
      version: v1

现在,我们可以运行其他测试,并验证应用程序将如何处理这些增加的响应时间和故障状况。

除了可以注入错误响应之外,服务网格技术还可以用来从环境中添加弹性。代理容器可以处理超时,实现断路器和隔板,而应用程序无需关心这些问题。

结论

容器编排和服务网格通过将应用程序的关注点转移到环境中来提高微服务应用程序的可测试性。有了服务抽象,我们就可以透明地替换服务或进行重新路由。服务网格不仅支持更复杂的路由,还允许我们注入故障或减慢响应,让应用程序处于压力之下,以此来验证其相应的行为。

更多资源

关于作者

 Sebastian Daschner 是一名自由 Java 顾问、作家和培训师,对编程和 Java 充满热情。他是“Architecting Modern Java EE Applications”一书的作者。Sebastian 正在参与 JCP,协助制定 Java EE 的未来标准,是 JAX-RS、JSON-P 和 Config 专家组的成员,并在各种开源项目上进行协作。由于在 Java 社区和生态系统中的贡献,他获得了 Java Champion、Oracle Developer Champion 和 2016 年 JavaOne Rockstar 的殊荣。除了 Java 之外,Sebastian 还是 Linux 和云原生技术的重要用户。他通过博客和Twitter(@DaschnerS)传播计算机科学实践。工作之余,他喜欢乘飞机或骑摩托车旅行。

查看英文原文 Improving Testability of Java Microservices with Container Orchestration and a Service Mesh

2018-08-09 18:272884
用户头像

发布了 731 篇内容, 共 482.2 次阅读, 收获喜欢 2008 次。

关注

评论

发布
暂无评论
发现更多内容

DataPipeline正式成为信创工委会会员单位!致力于为世界级用户提供更优质产品和服务

DataPipeline数见科技

大数据 数据融合 数据管理

毕业总结

菲尼克斯

架构实战营

花费近一年时间整理的Android核心知识清单,面试篇

欢喜学安卓

android 程序员 面试 移动开发

程序员是如何看待程序员的,程序员是做什么的?

阿里大佬的「算法界Offer收割机」火爆Github,一夜获上万star

Java 编程 程序员 架构师 计算机

语言特性实战(一)

南冥

海归硕士程序员吐槽:回国一个月都没找到工作,我书都白读了?

Optional 的使用会导致性能下降吗

小技术君

性能优化 Optional

架构之:REST和HATEOAS

程序那些事

架构 系统架构 Rest 软件架构

最壕58人逆天改命:四面阿里拿offer后,才发现师哥给的面试笔记有多强大

Java架构师迁哥

从零开始学习3D可视化之演示项目

ThingJS数字孪生引擎

大前端 可视化 3D 3D可视化

程序员工资高,到底程序员的工资有多高?你不了解的程序员!

关于程序猿的28个经典段子

应聘高级Android工程师历程感言,你不懂还不学?

欢喜学安卓

android 程序员 面试 移动开发

Linux安装与常用命令

IT视界

Linux linux命令 Linux安装

HarmonyOS走进西安电子科技大学国际双创周

科技汇

Rancher × Apache APISIX:极速部署更好用的开源网关和 Ingress Controller

API7.ai 技术团队

Kubernetes 网关 rancher APISIX

大专的我,闭关56天,含泪拿下阿里offer,五轮面试,六个小时灵魂拷问

程序员没两把刷子,就别送外卖了!!!

DEMO CHINA带着1000+投资人,500+好项目首登重庆

创业邦

创业 投资

异常是怎么被处理的?这题的答案不在源码里面。

why技术

面试 JVM 字节码

直接裂开!京东二面被问SpringBoot整合MongoDB,我不会啊

Java架构没有996

Java mongodb 程序员 后端 JAVA开发

Linux文件查找常用命令-详细笔记

学神来啦

云计算 Linux 运维

技术解码丨实时音视频与PSTN融合的解决方案

腾讯云音视频

腾讯云 音视频 PSTN

融云技术分享:全面揭秘亿级IM消息的可靠投递机制

JackJiang

即时通讯 IM 可靠消息最终一致 融云

「腾讯面试题」兔子试毒

iOS底层面试题(上篇)

程序员 面试

云原生时代,服务网格能给企业带来哪些价值?

行云创新

云原生 解决方案 服务网格

一萌妹子的面试经历,美团四面三小时,成功拿到Java岗offer

白亦杨

Java 编程 程序员 架构师 计算机

聊聊数据仓库建设

水滴

数据仓库 数仓 数仓架构 主数据管理 标签体系

SpringBoot中时间格式化的5种方法!

王磊

spring springboot

通过容器编排和服务网格来改进Java微服务的可测性_Java_Sebastian Daschner_InfoQ精选文章