网格化服务 java_准备好使用本机,服务网格化的Java Enterprise Cloud

网格化服务 java

重要要点

  • 服务网格透明地为微服务添加了必需的技术横切关注点。
  • 诸如路由,弹性或身份验证之类的问题成为服务网格的责任。
  • 应用程序代码变得更加精简,并更加关注实际的业务逻辑。
  • Istio通过Sidecar代理容器透明地增强了诸如Kubernetes Pod之类的工作负载。
  • 带有现代应用程序服务器的Java EE通过使开发人员能够实施精益业务逻辑,可以与云原生技术很好地集成。

Java EE,云原生和服务网格-听起来确实不太合适。 还是呢? 是否有可能开发满足可伸缩性,监视,跟踪或路由等问题的现代云原生Java Enterprise应用程序,而无需自己实现所有功能? 如果是这样,怎么办?

在微服务的企业环境中,面临着以一致的方式向多个或所有服务添加技术关注点(例如发现,安全性,监视,跟踪,路由或故障处理)的挑战。 软件团队可以使用不同的技术来实现其单独的服务,但是他们需要遵守组织标准。 添加诸如API网关之类的共享资产会使服务纠缠在一起,并以某种方式破坏了微服务架构的目的。 但是,也应避免冗余。

服务网格透明地增强了作为网格一部分的每个微服务,具有一致的技术问题。 这些增强功能以​​与技术无关的方式添加,而不会影响应用程序。 因此,该应用程序专注于实现业务逻辑。 环境将技术必要性放在首位。

仪器和3D打印机

我们将要使用的展示应用程序是一个仪器制造厂和一个3D打印机制造商机器人。 想象一下一个仪器手Craft.io品店SaaS应用程序,客户可以在其中订购手工乐器。 我们的商店不提供最优质的乐器,而仅将请求转发给制造商机器人后端,该机器人通过3D打印我们的乐器。

这两个云原生微服务在Java EE 8中实现,被部署到Kubernetes集群,并由Istio管理。

输入云原生技术

为了使用Kubernetes和Istio管理Java EE应用程序,我们需要将它们打包为容器。 Docker映像是通过定义Dockerfile创建的。 这些基础结构代码文件指定整个应用程序的运行时,包括配置,Java运行时(即JRE和应用程序容器)以及所需的操作系统二进制文件。 目标环境仅从该映像启动完全配置的容器。

下面显示了打包的乐器Craft.io品商店应用程序的Dockerfile,该文件使用包括OpenLiberty应用程序服务器的自定义基本映像:

FROM docker.example.com/open-liberty:1

COPY target/instrument-craft-shop.war $DEPLOYMENT_DIR

OpenLiberty基本映像已经包括运行应用程序服务器所需的内容。 应用程序的Dockerfile将添加潜在的配置,并在最后一步添加应用程序存档。 通过使用瘦部署工件方法,我们利用了Docker的写时复制文件系统,并获得了极快的构建和较短的传输时间。

生成的映像将在协调的环境中运行,在我们的示例中是Kubernetes集群。

因此,还成为应用程序存储库一部分的是Kubernetes环境的基础结构代码文件。 YAML描述符包括集群应如何运行,分发和组织我们的应用程序,我们的Docker容器。

下面显示了instrument-craft-shop的服务定义。 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 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

该服务将考虑与定义的选择器匹配的容器。 此处的app标签实际上是标准名称,与我们的应用匹配。 最好还定义一个version标签,以便在多个应用程序版本同时存在时能够进一步自定义服务路由。

仪表车间将由客户端从集群外部调用。 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与制造商机器人后端进行通信。 maker bot应用程序定义了类似的名为maker-bot Kubernetes服务和部署资源。

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

下图显示了maker bot客户端,该客户端是工具车车间应用程序的一部分:

@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支持connectTimeoutreadTimeout方法。 强烈建议设置这些超时以防止长时间阻塞线程。

如您所见,通过主机名maker-bot和匹配Kubernetes服务定义的端口9080来配置maker bot后端。 这使我们摆脱了服务发现配置,例如为不同的环境定义了不同的目标端点,IP地址或主机名。 该URL在所有Kubernetes集群环境中都是稳定的,并已适当解析。

输入Istio

我们将展示Istio,它是Java / JVM世界中最广泛使用的服务网格示例之一。

Istio透明地向应用程序添加了跨领域的技术问题。 它使用代理Sidecar容器增强了应用程序容器,这些容器捕获了来自主容器和去往主容器的入站和出站流量。 主应用程序容器连接到所需的服务,并且不了解代理。 我们可以将Istio视为面向方面的编程中的方面,以透明的方式添加到应用程序中。 Istio可以使用几种编排框架实现,包括Kubernetes

我们的示例应用程序已部署到Kubernetes集群,该集群通过Istio和自动侧车注入功能得到了增强。 边车注入器会自动将Istio代理容器添加到每个吊舱。

Istio Pilot负责配置有关路由和弹性的Sidecar代理。 我们在声明性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

此路由规则指定所有到instrument-craft-shop服务的流量都是到v1版实例的路由。 Istio资源以与Kubernetes资源相同的方式添加到集群,例如通过kubectl命令行。 现在,我们可以进一步完善这些路线。

厂商bot后端的以下路由规则增加了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

此过于严格的目标策略一次仅允许一个连接,并且将拒绝其他连接。 有多种方法可以配置电路的打开和关闭方式,需要针对特定​​的系统设置进行调整。

透明地添加到现有应用程序中的其他方面是监视,日志记录和跟踪以及身份验证。 Sidecar容器中包含的Envoy代理添加了这些跨领域关注点,并将其暴露于环境中。

DevOps工程师可以访问所需的信息,例如通过检查Grafana和Prometheus扩展或属于Istio集群的跟踪解决方案。 通过相互加密Sidecar代理之间的连接来添加身份验证。 用户可以添加自己的证书,还可以配置允许通信的策略。

结论

Java EE非常适合服务网格背后的想法。 诸如路由,弹性或身份验证之类的技术交叉问题成为服务网格环境的责任。

实际上,Java EE始终围绕着这个想法而构建。 应用程序本身应将自己与业务逻辑,实际要解决的问题领域结合起来。 这最终为应用程序的用户提供了价值。 但是,诸如生命周期管理,依赖项注入,事务或线程之类的技术职责是应用程序容器的一部分。

编排框架和服务网格进一步采用了这种方法,并使服务发现,弹性,身份验证,监视或跟踪环境负责。 因此,这些责任不再是应用程序代码的问题。 他们不应该; 应用程序应专注于实现业务逻辑。

将来,打包的应用程序将使用纯Java EE 8或Jakarta EE充分构建。 从应用程序外部添加了技术横切关注点。

如果域需要其他关注事项,例如与业务相关的指标,则可以通过集成第三方扩展(例如MicroProfile指标)来添加这些关注点。 使用支持MicroProfile的容器或将第三方库安装到应用程序容器作为较低的Docker映像层,仍然使我们能够利用精简部署工件的优势。 这个想法符合关注点分离的原则。

因此,将诸如Docker,Kubernetes和Istio之类的云原生技术与Java EE或未来的Jakarta EE结合起来,是实现生产型企业应用程序的面向未来的选择。

更多资源

翻译自: https://www.infoq.com/articles/cloud-native-service-mesh-java-ee/?topicPageSponsorship=c1246725-b0a7-43a6-9ef9-68102c8d48e1

网格化服务 java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值