【AICon】探索RAG 技术在实际应用中遇到的挑战及应对策略!AICon精华内容已上线73%>>> 了解详情
写点什么

基于云原生技术和服务网格的 Java EE

  • 2018-04-25
  • 本文字数:5065 字

    阅读完需:约 17 分钟

关键要点

  • 服务网格将所需的技术关注点透明地添加到微服务中。
  • 路由、弹性或认证等问题成为服务网格的职责。
  • 应用程序代码变得更加精简,并更多地关注实际的业务逻辑。
  • Istio 通过边车代理容器增强工作负载,例如 Kubernetes Pod。
  • Java EE 通过支持开发人员实施精益业务逻辑与云原生技术完美地集成在一起。

Java EE、云原生和服务网格——听起来似乎不应该把它们放在一起,又或者它们确实应该在一起呢?我们是否有可能在无需自己实现所有东西的情况下开发现代云原生 Java 企业应用程序来满足可扩展性、监控、跟踪或路由等问题?如果可以,那么该怎样实现?

采用微服务架构的企业面临着一个挑战,就是如何将服务发现、安全、监控、跟踪、路由或故障处理等技术问题以一致的方式添加到微服务当中。软件团队可以使用不同的技术来实现各自的服务,但他们需要遵守组织标准。通过添加共享资源(如 API 网关)将微服务耦合在一起,这在某种程度上破坏了微服务架构的原本意义。但不管怎样,我们应该避免冗余。

服务网格增强了每个微服务,而这些微服务是网格的一部分。这些增强功能以​​与技术无关的方式添加到系统中,不会影响到应用程序。应用程序专注于实现业务逻辑,由环境负责来处理技术依赖。

仪器和 3D 打印机

我们即将要给出的示例应用程序是仪器商店和 3D 打印机机器人。假设有这样的一个仪器商店 SaaS 应用程序,客户可以通过这个应用程序订购制作好的仪器。商店本身不直接提供仪器,而是将请求转发给通过 3D 打印技术生产仪器的机器人。

我们使用 Java EE 8 来实现这两种云原生微服务,部署到 Kubernetes 集群中,并由 Istio 来管理。

云原生技术简介

为了使用 Kubernetes 和 Istio 来管理 Java EE 应用程序,我们需要将它们打包成容器。 Docker 镜像是通过定义 Dockerfile 文件来创建的。这些文件指定了整个应用程序的运行时,包括配置、Java 运行时(即 JRE 和应用程序容器)以及所需的操作系统二进制文件。

下面的 Dockerfile 用于打包仪器商店应用程序,它使用了一个自定义基础镜像,其中包含了一个 OpenLiberty 应用服务器:

复制代码
FROM docker.example.com/open-liberty:1
COPY target/instrument-craft-shop.war $DEPLOYMENT_DIR

OpenLiberty 基础镜像里已经包含了运行应用程序服务器所必需的东西。Dockerfile 将添加可能需要用到的配置。通过使用这种简单的部署方法,我们不仅很好地利用了 Docker 的 Copy-On-Write 文件系统,而且带来了快速构建和缩短交付时间的可能性。

构建好的镜像将在编排环境中运行,在我们的例子里,就是要在 Kubernetes 群集中运行。

因此,与 Kubernetes 环境相关的文件也成为应用程序代码库的一部分。 YAML 描述符包含了集群将如何运行、分布和组织我们的应用程序及 Docker 容器。

以下显示了仪器商店服务的定义,Kubernetes 服务是对应用程序的逻辑抽象。

复制代码
kind: Service
apiVersion: v1
metadata:
  name: instrument-craft-shop
  labels:
    app: instrument-craft-shop
spec:
  selector:
    app: instrument-craft-shop
  ports:
    - port: 9080
      name: http

该服务将请求分发给正在运行的实例,容器则由 Kubernetes 来管理。Kubernetes 部署文件定义了如何执行 Kubernetes Pod(也就是实际运行的工作负载)以及需要多少副本:

复制代码
kind: Deployment
apiVersion: apps/v1beta1
metadata:
  name: instrument-craft-shop
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: instrument-craft-shop
        version: v1
    spec:
      containers:
      - name: instrument-craft-shop
        image: docker.example.com/instrument-craft-shop:1
        imagePullPolicy: IfNotPresent
      restartPolicy: Always

该服务将采用与定义的选择器相匹配的 Pod。这里的应用程序标签实际上是标准名称,与我们的应用程序相匹配。我们最好可以再定义一个版本标签,以便在多个应用程序版本同时存在的时候能够进一步自定义服务路由。

集群外部的客户端将会调用仪器商店应用程序。Kubernetes 的摄入资源将入口流量路由到相应的服务:

复制代码
kind: Ingress
apiVersion: extensions/v1beta1
metadata:
  name: instrument-craft-shop
  annotations:
    kubernetes.io/ingress.class: istio
spec:
  rules:
  - http:
      paths:
      - path: /instrument-craft-shop/.*
        backend:
          serviceName: instrument-craft-shop
          servicePort: 9080

ingress.class 注释指定了将 istio 作为入口实现,因此 Kubernetes 将为我们部署正确的 Istio 入口。

仪器商店应用程序将通过 HTTP 与后端的机器人应用程序进行通信。机器人应用程序定义了类似的 Kubernetes 服务和部署资源,名为 maker-bot。

因为这两个应用程序都是 Kubernetes 群集的一部分,所以它们可以使用服务定义作为主机名进行通信。 Kubernetes 通过 DNS 来解析服务名称。

下面是机器人客户端的代码:

复制代码
@ApplicationScoped
public class MakerBot {
    private Client client;
    private WebTarget target;
    @PostConstruct
    private void initClient() {
        client = ClientBuilder.newBuilder()
                .connectTimeout(1, TimeUnit.SECONDS)
                .readTimeout(3, TimeUnit.SECONDS)
                .build();
        target = client.target("http://maker-bot:9080/maker-bot/resources/jobs");
    }
    public void printInstrument(InstrumentType type) {
        JsonObject requestBody = createRequestBody(type);
        Response response = sendRequest(requestBody);
        validateResponse(response);
    }
    private JsonObject createRequestBody(InstrumentType type) {
        return Json.createObjectBuilder()
                .add("instrument", type.name().toLowerCase())
                .build();
    }
    private Response sendRequest(JsonObject requestBody) {
        try {
            return target
                .request()
                .post(Entity.json(requestBody));
        } catch (Exception e) {
            throw new IllegalStateException("Could not print instrument, reason: "
                    + e.getMessage(), e);
        }
    }
    private void validateResponse(Response response) {
        if (response.getStatusInfo().getFamily() != Response.Status.Family.SUCCESSFUL)
            throw new IllegalStateException("Could not print instrument, status: "
                    + response.getStatus());
    }
    @PreDestroy
    private void closeClient() {
        client.close();
    }
}

从 Java EE 8 开始,JAX-RS 客户端构建器 API 支持 connectTimeout 和 readTimeout 方法。我们强烈建议设置这些超时参数,以防止长时间阻塞线程。

正如你所看到的,机器人应用程序通过主机名称 maker-bot 和端口 9080(与 Kubernetes 的服务定义相匹配)进行配置。我们因此能够摆脱服务发现配置,例如为不同的环境定义不同的目标端点、IP 地址或主机名称。不管在哪个 Kubernetes 集群环境中,URL 都是稳定不变的,并可以被恰当地解析。

Istio 登场

接下来我们将演示 Istio,它是 Java/JVM 领域使用最为广泛的服务网格示例之一。

Istio 将技术切面关注点透明地添加到应用程序中。它通过代理边车容器增强了应用程序 Pod,捕获主容器的流入和流出流量。主应用程序容器连接到必要的服务上,但不知道代理服务器的存在。我们可以将 Istio 视为一个切面,就像在面向方面的编程模型里一样,它们被透明地添加到应用程序中。Istio 可以使用多种编排框架实现,包括 Kubernetes。

我们的示例应用程序被部署到 Kubernetes 集群中,这个集群使用了 Istio 和自动边车注入。边车注入自动将 Istio 代理容器添加到每个 Pod。

Istio Pilot 负责配置边车代理的路由规则和弹性参数。我们在 YAML 文件中配置 Istio 切面,类似 Kubernetes 的资源。

根据最佳实践,我们为相应的应用程序服务添加默认路由:

复制代码
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: instrument-craft-shop-default
spec:
  destination:
    name: instrument-craft-shop
  precedence: 1
  route:
  - weight: 100
    labels:
      version: v1

该路由规则指定了所有流向仪器商店应用程序的流量都将被路由到版本为 v1 的实例上。Istio 资源以与 Kubernetes 资源相同的方式添加到群集中,例如通过 kubectl 命令行。我们现在可以进一步增强这些路由规则。

以下路由规则给后端机器人增加了一个 2 秒种的超时时间:

复制代码
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: maker-bot-default
spec:
destination:
name: maker-bot
precedence: 1
route:
- weight: 100
labels:
version: v1
httpReqTimeout:
simpleTimeout:
timeout: 2s

这个超时时间与其他应用程序级别的超时时间是相互独立的,一旦被触发,代理就会返回 503 错误。这样可以防止系统出现无限制的阻塞,即使没有为 MakerBot 类的 JAX-RS 客户端配置超时时间。客户端将收到超时通知,以先触发的那个超时时间为准。

Istio 的另一个特点是可以通过添加回路断路器来以防止应用程序出现过载和整体失效。我们为后端机器人目标策略添加了一个回路断路器:

复制代码
apiVersion: config.istio.io/v1alpha2
kind: DestinationPolicy
metadata:
  name: maker-bot-circuit-breaker
spec:
  destination:
    name: maker-bot
  circuitBreaker:
    simpleCb:
      httpConsecutiveErrors: 1
      sleepWindow: 10s
      httpDetectionInterval: 10s
      httpMaxEjectionPercent: 100
      maxConnections: 1
      httpMaxPendingRequests: 1
      httpMaxRequestsPerConnection: 1

这个目标策略一次只允许一个连接,并会拒绝其他连接。我们还可以配置回路断路器如何再次打开和关闭,具体需要根据特定的系统设置进行调整。

被透明地添加到现有应用程序中的还有监视、日志和跟踪,以及身份认证。边车容器中包含了 Envoy 代理,我们就是通过它们来添加这些横切面关注点,并把它们暴露给外部环境。

DevOps 工程师可以通过检查 Grafana 和 Prometheus 扩展或跟踪解决方案来访问他们需要的信息。我们通过加密边车代理之间的连接来添加认证。用户可以添加自己的证书,并配置通信策略。

结论

Java EE 与服务网格背后的想法相得益彰,而技术横切面问题(如路由、弹性或认证)成为服务网格环境的职责。

实际上,Java EE 是基于这个想法而构建起来的。应用程序本身应该更多地关注业务逻辑,解决实际的领域问题,为应用程序的用户提供价值。而处理生命周期管理、依赖注入、事务或线程等问题则是应用程序容器的职责。

编排框架和服务网格进一步采用这种方法,进行服务发现、增强弹性、认证、监控或追踪。因此这些问题不再是应用程序的关注点,应用程序应该专注于实现业务逻辑。

在未来,我们将使用存粹的 Java EE 8 或 Jakarta EE 来构建和打包应用程序,然后从应用程序外部添加技术横切面。

如果域名需要额外的关注点,例如与业务相关的指标,则可以通过集成第三方扩展来添加,例如 MicroProfile Metrics。使用支持 MicroProfile 的容器,或者将第三方库安装到应用程序容器中,作为底层的 Docker 镜像层,我们仍然能够利用精简部署的优势。这种想法符合关注点分离原则。

Docker、Kubernetes 和 Istio 等云原生技术与 Java EE 或将来的 Jakarta EE 相结合,是未来企业应用程序的最佳选择。

更多资源

关于作者

Sebastian Daschner 是一名独立 Java 顾问、作者和培训师,对编程和 Java(EE)充满热情。他是“架构现代 Java EE 应用程序”一书的作者。Sebastian 积极参与 JCP,帮助制定未来的 Java EE 标准,服务于 JAX-RS、JSON-P 和 Config 专家组,并在各种开源项目上进行合作。他因为在 Java 社区和生态系统中的贡献而被授予了 Java Champion、Oracle Developer Champion 和 2016 年 JavaOne Rockstar 的荣誉。除 Java 外,Sebastian 还是 Linux 和云原生技术的重要用户。他通过 @DaschnerS 在博客、新闻组和推特上传播计算机科学实践。平常他还喜欢搭飞机或骑摩托环游世界。

查看英文原文 Get Ready for Cloud Native, Service-Meshed Java Enterprise

2018-04-25 18:051742
用户头像

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

关注

评论

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

干货复盘 | 银行数智化转型十大趋势

易观分析

金融 银行 数智化转型

这次6张图带你彻底搞懂RocketMQ是怎么保存偏移量的?

程序员小毕

Java 程序员 面试 RocketMQ 消息中间件

开源公开课丨ChunJun数据传输模块介绍

袋鼠云数栈

史上最全的Java并发系列之并发编程的挑战

自然

多线程 并发 8月月更

表达式转换-中缀转后缀表达式后计算-数据结构与算法

清风莫追

算法与数据结构 8月月更

秒云获得阿里云首批产品生态集成认证,携手共建云原生智能运维生态服务

MIAOYUN

智能运维 智能运维AIOps 阿里云产品生态集成认证

RT-Thread记录(八、理解 RT-Thread 内存管理)

矜辰所致

内存管理 RT-Thread 8月月更

SAP ABAP Netweaver 服务器的标准登录方式讲解

Jerry Wang

web开发 web服务器 abap Netweaver 8月月更

SAP ABAP 关键字语法图和 ABAP 代码自动生成工具 Code Composer

Jerry Wang

Java SAP abap commerce 8月月更

预约直播 | 深度学习编译器技术趋势与阿里云BladeDISC的编译器实践

阿里云大数据AI技术

深度学习 阿里云 编译器

破解双中台困局:万家数科 x StarRocks 数字化技术实践

StarRocks

大数据

史上最全的Java并发系列之Java并发机制的底层实现原理

自然

多线程 并发 8月月更

2分钟一图看懂AntDB数据库产品

亚信AntDB数据库

AntDB 国产数据库 aisware antdb

IPv5是什么意思?到底有没有IPv5?

郑州埃文科技

ipv6 互联网协议 ipv5

终于!极狐GitLab 支持 ARM 啦!

极狐GitLab

DevOps 敏捷开发 arm 极狐GitLab 嵌入式硬件

史上最全的Java并发系列之Java并发容器和框架

自然

多线程 并发 8月月更

华贵保险连续五年获得“A级纳税信用企业”

江湖老铁

合合信息加入元脑生态 携手浪潮信息推动金融AI应用创新

合合技术团队

AI 智能时代 合合信息 人工智能’ 浪潮

Alluxio on Amazon EMR 集成实践

亚马逊云科技 (Amazon Web Services)

实践 集成 Tech 专栏

二分查找:一种效率较高的查找方法

华为云开发者联盟

开发 二分查找 查找 区间

2022年中国足球赛事营销洞察

易观分析

营销 报告 足球

【云原生】Docker 进阶 -- 数据卷使用与实战练习

Bug终结者

Docker 阿里云 云原生 8月月更

2篇论文入选KDD 2022!腾讯广告持续探索效果提升新思路

Geek_2d6073

CSO视角:Sigstore如何保障软件供应链安全?

SEAL安全

软件供应链安全 OpenSSF

java就业培训班如何选择?

小谷哥

计算机专业和培训出来的前端程序员的区别

小谷哥

如何利用CANN DVPP进行图片的等比例缩放?

华为云开发者联盟

人工智能 图像 昇腾AI

大数据程序员参加培训好还是自学好

小谷哥

旺链科技成为湖南省区块链协会理事单位

旺链科技

区块链 区块链技术 产业区块链

前端培训学完课程后如何找工作?

小谷哥

大数据培训学习软件工程师机构靠谱吗

小谷哥

基于云原生技术和服务网格的Java EE_Java_Sebastian Daschner_InfoQ精选文章