标签 微服务 下的文章

研究发现,硬盘一般三年就坏

根据一家安全数据恢复公司的研究,在它收到的 2007 个有缺陷的硬盘驱动器中,它发现这些硬盘的平均故障时间为 2 年 10 个月。而另外一家备份和云存储公司 Backblaze,以详细的硬盘和固态硬盘故障分析而闻名,在其检查的 17155 个故障硬盘中,硬盘故障的平均年龄为 2 年 6 个月。

消息来源:Ars Technica
老王点评:说实话这个数据有点令人惊讶地短。你的硬盘一般能用多久?

从微服务转换到单体应用,减少了 90% 的成本

一份来自 Prime Video 团队的亚马逊案例研究,评估了如何通过从微服务架构转向单体来节省资金。该团队最初创建了一个由 AWS Step Functions 协调的分布式组件的解决方案,这是一个基于状态机和任务的无服务器协调服务。最终他们的研究表明,微服务和无服务器组件在大规模下确实是有效的工具,但是否使用它们而不是单体应用,必须根据具体情况来决定。通过将服务转移到单体应用上,使得基础设施成本降低了 90% 以上。

消息来源:Dev Class
老王点评:微服务并不是灵丹妙药,云计算也不是银弹,AWS 推荐的也不一定是成本最优的。

Mojo:一种具有 C 语言速度的 Python 超集

Mojo 希望将 Python 的可用性与 C 语言的速度结合起来。Mojo 通过硬件加速,在运行特定数字算法时比 Python 快 35,000 倍。它是建立在下一代编译器技术之上的,使你能够定义零成本的抽象,并受益于类似 Rust 的内存安全。Mojo 代码可以访问各种人工智能调整的硬件功能,因此,对于某些种类的算法,它的速度远远超过了原版 Python,比如在 AWS 机器上运行 Mandelbrot 算法只需 0.03 秒,而 Python 3.10.9 则需要 1027 秒。有人称赞“Mojo 可能是几十年来最大的编程语言进步”。该语言还在早期开发中,仍然有一些缺失的部分,并且尚未开源。

消息来源:The Register
老王点评:会有这种既要又要的好事吗?让我们拭目以待。

微服务遵循领域驱动设计(DDD),与开发平台无关。Python 微服务也不例外。Python3 的面向对象特性使得按照 DDD 对服务进行建模变得更加容易。本系列的第 10 部分演示了如何将用户管理系统的查找服务作为 Python 微服务部署在 Kubernetes 上。

微服务架构的强大之处在于它的多语言性。企业将其功能分解为一组微服务,每个团队自由选择一个平台。

我们的用户管理系统已经分解为四个微服务,分别是添加、查找、搜索和日志服务。添加服务在 Java 平台上开发并部署在 Kubernetes 集群上,以实现弹性和可扩展性。这并不意味着其余的服务也要使用 Java 开发,我们可以自由选择适合个人服务的平台。

让我们选择 Python 作为开发查找服务的平台。查找服务的模型已经设计好了(参考 2022 年 3 月份的文章),我们只需要将这个模型转换为代码和配置。

Pythonic 方法

Python 是一种通用编程语言,已经存在了大约 30 年。早期,它是自动化脚本的首选。然而,随着 Django 和 Flask 等框架的出现,它的受欢迎程度越来越高,现在各种领域中都在应用它,如企业应用程序开发。数据科学和机器学习进一步推动了它的发展,Python 现在是三大编程语言之一。

许多人将 Python 的成功归功于它容易编码。这只是一部分原因。只要你的目标是开发小型脚本,Python 就像一个玩具,你会非常喜欢它。然而,当你进入严肃的大规模应用程序开发领域时,你将不得不处理大量的 ifelse,Python 变得与任何其他平台一样好或一样坏。例如,采用一种面向对象的方法!许多 Python 开发人员甚至可能没意识到 Python 支持类、继承等功能。Python 确实支持成熟的面向对象开发,但是有它自己的方式 -- Pythonic!让我们探索一下!

领域模型

AddService 通过将数据保存到一个 MySQL 数据库中来将用户添加到系统中。FindService 的目标是提供一个 REST API 按用户名查找用户。域模型如图 1 所示。它主要由一些值对象组成,如 User 实体的NamePhoneNumber 以及 UserRepository

图 1: 查找服务的域模型

让我们从 Name 开始。由于它是一个值对象,因此必须在创建时进行验证,并且必须保持不可变。基本结构如所示:

class Name:
    value: str
    def __post_init__(self):
        if self.value is None or len(self.value.strip()) < 8 or len(self.value.strip()) > 32:
            raise ValueError("Invalid Name")

如你所见,Name 包含一个字符串类型的值。作为后期初始化的一部分,我们会验证它。

Python 3.7 提供了 @dataclass 装饰器,它提供了许多开箱即用的数据承载类的功能,如构造函数、比较运算符等。如下是装饰后的 Name 类:

from dataclasses import dataclass

@dataclass
class Name:
    value: str
    def __post_init__(self):
        if self.value is None or len(self.value.strip()) < 8 or len(self.value.strip()) > 32:
            raise ValueError("Invalid Name")

以下代码可以创建一个 Name 对象:

name = Name("Krishna")

value 属性可以按照如下方式读取或写入:

name.value = "Mohan"
print(name.value)

可以很容易地与另一个 Name 对象比较,如下所示:

other = Name("Mohan")
if name == other:
    print("same")

如你所见,对象比较的是值而不是引用。这一切都是开箱即用的。我们还可以通过冻结对象使对象不可变。这是 Name 值对象的最终版本:

from dataclasses import dataclass

@dataclass(frozen=True)
class Name:
    value: str
    def __post_init__(self):
        if self.value is None or len(self.value.strip()) < 8 or len(self.value.strip()) > 32:
            raise ValueError("Invalid Name")

PhoneNumber 也遵循类似的方法,因为它也是一个值对象:

@dataclass(frozen=True)
class PhoneNumber:
    value: int
    def __post_init__(self):
        if self.value < 9000000000:
            raise ValueError("Invalid Phone Number")

User 类是一个实体,不是一个值对象。换句话说,User 是可变的。以下是结构:

from dataclasses import dataclass
import datetime

@dataclass
class User:
    _name: Name
    _phone: PhoneNumber
    _since: datetime.datetime

    def __post_init__(self):
        if self._name is None or self._phone is None:
            raise ValueError("Invalid user")
        if self._since is None:
            self.since = datetime.datetime.now()

你能观察到 User 并没有冻结,因为我们希望它是可变的。但是,我们不希望所有属性都是可变的。标识字段如 _name_since 是希望不会修改的。那么,这如何做到呢?

Python3 提供了所谓的描述符协议,它会帮助我们正确定义 getter 和 setter。让我们使用 @property 装饰器将 getter 添加到 User 的所有三个字段中。

@property
def name(self) -> Name:
    return self._name

@property
def phone(self) -> PhoneNumber:
    return self._phone

@property
def since(self) -> datetime.datetime:
    return self._since

phone 字段的 setter 可以使用 @<字段>.setter 来装饰:

@phone.setter
def phone(self, phone: PhoneNumber) -> None:
    if phone is None:
        raise ValueError("Invalid phone")
    self._phone = phone

通过重写 __str__() 函数,也可以为 User 提供一个简单的打印方法:

def __str__(self):
    return self.name.value + " [" + str(self.phone.value) + "] since " + str(self.since)

这样,域模型的实体和值对象就准备好了。创建异常类如下所示:

class UserNotFoundException(Exception):
    pass

域模型现在只剩下 UserRepository 了。Python 提供了一个名为 abc 的有用模块来创建抽象方法和抽象类。因为 UserRepository 只是一个接口,所以我们可以使用 abc 模块。

任何继承自 abc.ABC 的类都将变为抽象类,任何带有 @abc.abstractmethod 装饰器的函数都会变为一个抽象函数。下面是 UserRepository 的结构:

from abc import ABC, abstractmethod

class UserRepository(ABC):
    @abstractmethod
    def fetch(self, name:Name) -> User:
        pass

UserRepository 遵循仓储模式。换句话说,它在 User 实体上提供适当的 CRUD 操作,而不会暴露底层数据存储语义。在本例中,我们只需要 fetch() 操作,因为 FindService 只查找用户。

因为 UserRepository 是一个抽象类,我们不能从抽象类创建实例对象。创建对象必须依赖于一个具体类实现这个抽象类。数据层 UserRepositoryImpl 提供了 UserRepository 的具体实现:

class UserRepositoryImpl(UserRepository):
    def fetch(self, name:Name) -> User:
        pass

由于 AddService 将用户数据存储在一个 MySQL 数据库中,因此 UserRepositoryImpl 也必须连接到相同的数据库去检索数据。下面是连接到数据库的代码。注意,我们正在使用 MySQL 的连接库。

from mysql.connector import connect, Error

class UserRepositoryImpl(UserRepository):
    def fetch(self, name:Name) -> User:
        try:
            with connect(
                    host="mysqldb",
                    user="root",
                    password="admin",
                    database="glarimy",
                ) as connection:
                with connection.cursor() as cursor:
                    cursor.execute("SELECT * FROM ums_users where name=%s", (name.value,))
                    row = cursor.fetchone()
                    if cursor.rowcount == -1:
                        raise UserNotFoundException()
                    else:
                        return User(Name(row[0]), PhoneNumber(row[1]), row[2])
        except Error as e:
            raise e

在上面的片段中,我们使用用户 root / 密码 admin 连接到一个名为 mysqldb 的数据库服务器,使用名为 glarimy 的数据库(模式)。在演示代码中是可以包含这些信息的,但在生产中不建议这么做,因为这会暴露敏感信息。

fetch() 操作的逻辑非常直观,它对 ums_users 表执行 SELECT 查询。回想一下,AddService 正在将用户数据写入同一个表中。如果 SELECT 查询没有返回记录,fetch() 函数将抛出 UserNotFoundException 异常。否则,它会从记录中构造 User 实体并将其返回给调用者。这没有什么特殊的。

应用层

最终,我们需要创建应用层。此模型如图 2 所示。它只包含两个类:控制器和一个 DTO。

图 2: 添加服务的应用层

众所周知,一个 DTO 只是一个没有任何业务逻辑的数据容器。它主要用于在 FindService 和外部之间传输数据。我们只是提供了在 REST 层中将 UserRecord 转换为字典以便用于 JSON 传输:

class UserRecord:
    def toJSON(self):
        return {
            "name": self.name,
            "phone": self.phone,
            "since": self.since
        }

控制器的工作是将 DTO 转换为用于域服务的域对象,反之亦然。可以从 find() 操作中观察到这一点。

class UserController:

    def __init__(self):
        self._repo = UserRepositoryImpl()

    def find(self, name: str):
        try:
            user: User = self._repo.fetch(Name(name))
            record: UserRecord = UserRecord()
            record.name = user.name.value
            record.phone = user.phone.value
            record.since = user.since
            return record
        except UserNotFoundException as e:
            return None

find() 操作接收一个字符串作为用户名,然后将其转换为 Name 对象,并调用 UserRepository 获取相应的 User 对象。如果找到了,则使用检索到的 User` 对象创建UserRecord。回想一下,将域对象转换为 DTO 是很有必要的,这样可以对外部服务隐藏域模型。

UserController 不需要有多个实例,它也可以是单例的。通过重写 __new__,可以将其建模为一个单例。

class UserController:
    def __new__(self):
        if not hasattr(self, ‘instance’):
            self.instance = super().__new__(self)
        return self.instance

    def __init__(self):
        self._repo = UserRepositoryImpl()

    def find(self, name: str):
        try:
            user: User = self._repo.fetch(Name(name))
            record: UserRecord = UserRecord()
            record.name = user.name.getValue()
            record.phone = user.phone.getValue()
            record.since = user.since
            return record
        except UserNotFoundException as e:
            return None

我们已经完全实现了 FindService 的模型,剩下的唯一任务是将其作为 REST 服务公开。

REST API

FindService 只提供一个 API,那就是通过用户名查找用户。显然 URI 如下所示:

GET /user/{name}

此 API 希望根据提供的用户名查找用户,并以 JSON 格式返回用户的电话号码等详细信息。如果没有找到用户,API 将返回一个 404 状态码。

我们可以使用 Flask 框架来构建 REST API,它最初的目的是使用 Python 开发 Web 应用程序。除了 HTML 视图,它还进一步扩展到支持 REST 视图。我们选择这个框架是因为它足够简单。 创建一个 Flask 应用程序:

from flask import Flask
app = Flask(__name__)

然后为 Flask 应用程序定义路由,就像函数一样简单:

@app.route('/user/<name>')
def get(name):
    pass

注意 @app.route 映射到 API /user/<name>,与之对应的函数的 get()

如你所见,每次用户访问 API 如 http://server:port/user/Krishna 时,都将调用这个 get() 函数。Flask 足够智能,可以从 URL 中提取 Krishna 作为用户名,并将其传递给 get() 函数。

get() 函数很简单。它要求控制器找到该用户,并将其与通常的 HTTP 头一起打包为 JSON 格式后返回。如果控制器返回 None,则 get() 函数返回合适的 HTTP 状态码。

from flask import jsonify, abort

controller = UserController()
record = controller.find(name)
if record is None:
    abort(404)
else:
    resp = jsonify(record.toJSON())
    resp.status_code = 200
    return resp

最后,我们需要 Flask 应用程序提供服务,可以使用 waitress 服务:

from waitress import serve
serve(app, host="0.0.0.0", port=8080)

在上面的片段中,应用程序在本地主机的 8080 端口上提供服务。最终代码如下所示:

from flask import Flask, jsonify, abort
from waitress import serve

app = Flask(__name__)

@app.route('/user/<name>')
def get(name):
    controller = UserController()
    record = controller.find(name)
    if record is None:
        abort(404)
    else:
        resp = jsonify(record.toJSON())
        resp.status_code = 200
        return resp

serve(app, host="0.0.0.0", port=8080)

部署

FindService 的代码已经准备完毕。除了 REST API 之外,它还有域模型、数据层和应用程序层。下一步是构建此服务,将其容器化,然后部署到 Kubernetes 上。此过程与部署其他服务妹有任何区别,但有一些 Python 特有的步骤。

在继续前进之前,让我们来看下文件夹和文件结构:

+ ums-find-service
+ ums
- domain.py
- data.py
- app.py
- Dockerfile
- requirements.txt
- kube-find-deployment.yml

如你所见,整个工作文件夹都位于 ums-find-service 下,它包含了 ums 文件夹中的代码和一些配置文件,例如 Dockerfilerequirements.txtkube-find-deployment.yml

domain.py 包含域模型,data.py 包含 UserRepositoryImplapp.py 包含剩余代码。我们已经阅读过代码了,现在我们来看看配置文件。

第一个是 requirements.txt,它声明了 Python 系统需要下载和安装的外部依赖项。我们需要用查找服务中用到的每个外部 Python 模块来填充它。如你所见,我们使用了 MySQL 连接器、Flask 和 Waitress 模块。因此,下面是 requirements.txt 的内容。

Flask==2.1.1
Flask_RESTful
mysql-connector-python
waitress

第二步是在 Dockerfile 中声明 Docker 相关的清单,如下:

FROM python:3.8-slim-buster

WORKDIR /ums
ADD ums /ums
ADD requirements.txt requirements.txt
RUN pip3 install -r requirements.txt

EXPOSE 8080
ENTRYPOINT ["python"]
CMD ["/ums/app.py"]

总的来说,我们使用 Python 3.8 作为基线,除了移动 requirements.txt 之外,我们还将代码从 ums 文件夹移动到 Docker 容器中对应的文件夹中。然后,我们指示容器运行 pip3 install 命令安装对应模块。最后,我们向外暴露 8080 端口(因为 waitress 运行在此端口上)。

为了运行此服务,我们指示容器使用使用以下命令:

python /ums/app.py

一旦 Dockerfile 准备完成,在 ums-find-service 文件夹中运行以下命令,创建 Docker 镜像:

docker build -t glarimy/ums-find-service

它会创建 Docker 镜像,可以使用以下命令查找镜像:

docker images

尝试将镜像推送到 Docker Hub,你也可以登录到 Docker。

docker login
docker push glarimy/ums-find-service

最后一步是为 Kubernetes 部署构建清单。

在之前的文章中,我们已经介绍了如何建立 Kubernetes 集群、部署和使用服务的方法。我假设仍然使用之前文章中的清单文件来部署添加服务、MySQL、Kafka 和 Zookeeper。我们只需要将以下内容添加到 kube-find-deployment.yml 文件中:

apiVersion: apps/v1
kind: Deployment
metadata:
name: ums-find-service
labels:
app: ums-find-service
spec:
replicas: 3
selector:
matchLabels:
app: ums-find-service
template:
metadata:
labels:
app: ums-find-service
spec:
containers:
- name: ums-find-service
image: glarimy/ums-find-service
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: ums-find-service
labels:
name: ums-find-service
spec:
type: LoadBalancer
ports:
- port: 8080
selector:
app: ums-find-service

上面清单文件的第一部分声明了 glarimy/ums-find-service 镜像的 FindService,它包含三个副本。它还暴露 8080 端口。清单的后半部分声明了一个 Kubernetes 服务作为 FindService 部署的前端。请记住,在之前文章中,mysqldb 服务已经是上述清单的一部分了。

运行以下命令在 Kubernetes 集群上部署清单文件:

kubectl create -f kube-find-deployment.yml

部署完成后,可以使用以下命令验证容器组和服务:

kubectl get services

输出如图 3 所示:

图 3: Kubernetes 服务

它会列出集群上运行的所有服务。注意查找服务的外部 IP,使用 curl 调用此服务:

curl http://10.98.45.187:8080/user/KrishnaMohan

注意:10.98.45.187 对应查找服务,如图 3 所示。

如果我们使用 AddService 创建一个名为 KrishnaMohan 的用户,那么上面的 curl 命令看起来如图 4 所示:

图 4: 查找服务

用户管理系统(UMS)的体系结构包含 AddServiceFindService,以及存储和消息传递所需的后端服务,如图 5 所示。可以看到终端用户使用 ums-add-service 的 IP 地址添加新用户,使用 ums-find-service 的 IP 地址查找已有用户。每个 Kubernetes 服务都由三个对应容器的节点支持。还要注意:同样的 mysqldb 服务用于存储和检索用户数据。

图 5: UMS 的添加服务和查找服务

其他服务

UMS 系统还包含两个服务:SearchServiceJournalService。在本系列的下一部分中,我们将在 Node 平台上设计这些服务,并将它们部署到同一个 Kubernetes 集群,以演示多语言微服务架构的真正魅力。最后,我们将观察一些与微服务相关的设计模式。


via: https://www.opensourceforu.com/2022/09/python-microservices-using-flask-on-kubernetes/

作者:Krishna Mohan Koyya 选题:lkxed 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在微服务环境中,服务网格为开发和运营提供了好处。

 title=

很多开发者不知道为什么要关心 服务网格 Service Mesh 。这是我在开发者见面会、会议和实践研讨会上关于云原生架构的微服务开发的演讲中经常被问到的问题。我的回答总是一样的:“只要你想简化你的微服务架构,它就应该运行在 Kubernetes 上。”

关于简化,你可能也想知道,为什么分布式微服务必须设计得如此复杂才能在 Kubernetes 集群上运行。正如本文所解释的那样,许多开发人员通过服务网格解决了微服务架构的复杂性,并通过在生产中采用服务网格获得了额外的好处。

什么是服务网格?

服务网格是一个专门的基础设施层,用于提供一个透明的、独立于代码的 (polyglot) 方式,以消除应用代码中的非功能性微服务能力。

 title=

为什么服务网格对开发者很重要

当开发人员将微服务部署到云时,无论业务功能如何,他们都必须解决非功能性微服务功能,以避免级联故障。这些功能通常可以体现在服务发现、日志、监控、 韧性 resiliency 、认证、 弹性 elasticity 和跟踪等方面。开发人员必须花费更多的时间将它们添加到每个微服务中,而不是开发实际的业务逻辑,这使得微服务变得沉重而复杂。

随着企业加速向云计算转移,服务网格 可以提高开发人员的生产力。Kubernetes 加服务网格平台不需要让服务负责处理这些复杂的问题,也不需要在每个服务中添加更多的代码来处理云原生的问题,而是负责向运行在该平台上的任何应用(现有的或新的,用任何编程语言或框架)提供这些服务。那么微服务就可以轻量级,专注于其业务逻辑,而不是云原生的复杂性。

为什么服务网格对运维很重要

这并没有回答为什么运维团队需要关心在 Kubernetes 上运行云原生微服务的服务网格。因为运维团队必须确保在 Kubernetes 环境上的大型混合云和多云上部署新的云原生应用的强大安全性、合规性和可观察性。

服务网格由一个用于管理代理路由流量的控制平面和一个用于注入 边车 Sidecar 的数据平面组成。边车允许运维团队做一些比如添加第三方安全工具和追踪所有服务通信中的流量,以避免安全漏洞或合规问题。服务网格还可以通过在图形面板上可视化地跟踪指标来提高观察能力。

如何开始使用服务网格

对于开发者和运维人员,以及从应用开发到平台运维来说,服务网格可以更有效地管理云原生功能。

你可能想知道从哪里开始采用服务网格来配合你的微服务应用和架构。幸运的是,有许多开源的服务网格项目。许多云服务提供商也在他们的 Kubernetes 平台中提供 服务网格。

 title=

你可以在 CNCF Service Mesh Landscape 页面中找到最受欢迎的服务网格项目和服务的链接。


via: https://opensource.com/article/21/3/service-mesh

作者:Daniel Oh 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

通过这三个工具和框架,为你的企业级 Java 应用和你的职业生涯提供助力。

 title=

尽管在 Kubernetes 上广泛使用 PythonGoNode.js 实现 人工智能 和机器学习应用以及 无服务函数,但 Java 技术仍然在开发企业应用中发挥着关键作用。根据 开发者经济学 的数据,在 2020 年第三季度,全球有 800 万名企业 Java 开发者。

虽然这门语言已经存在了超过 25 年,但 Java 世界中总是有新的趋势、工具和框架,可以为你的应用和你的职业生涯赋能。

绝大多数 Java 框架都是为具有动态行为的长时间运行的进程而设计的,这些动态行为用于运行可变的应用服务器,例如物理服务器和虚拟机。自从 Kubernetes 容器在 2014 年发布以来,情况已经发生了变化。在 Kubernetes 上使用 Java 应用的最大问题是通过减少内存占用、加快启动和响应时间以及减少文件大小来优化应用性能。

3 个值得考虑的新 Java 框架和工具

Java 开发人员也一直在寻找更简便的方法,将闪亮的新开源工具和项目集成到他们的 Java 应用和日常工作中。这极大地提高了开发效率,并激励更多的企业和个人开发者继续使用 Java 栈。

当试图满足上述企业 Java 生态系统的期望时,这三个新的 Java 框架和工具值得你关注。

1、Quarkus

Quarkus 旨在以惊人的快速启动时间、超低的常驻内存集(RSS)和高密度内存利用率,在 Kubernetes 等容器编排平台中开发云原生的微服务和无服务。根据 JRebel 的 第九届全球 Java 开发者生产力年度报告,Java 开发者对 Quarkus 的使用率从不到 1% 上升到 6%,MicronautVert.x 均从去年的 1% 左右分别增长到 4% 和 2%。

2、Eclipse JKube

Eclipse JKube 使 Java 开发者能够使用 DockerJibSource-To-Image 构建策略,基于云原生 Java 应用构建容器镜像。它还能在编译时生成 Kubernetes 和 OpenShift 清单,并改善开发人员对调试、观察和日志工具的体验。

3、MicroProfile

MicroProfile 解决了与优化企业 Java 的微服务架构有关的最大问题,而无需采用新的框架或重构整个应用。此外,MicroProfile 规范(即 Health、Open Tracing、Open API、Fault Tolerance、Metrics、Config)继续与 Jakarta EE 的实现保持一致。

总结

很难说哪个 Java 框架或工具是企业 Java 开发人员实现的最佳选择。只要 Java 栈还有改进的空间,并能加速企业业务的发展,我们就可以期待新的框架、工具和平台的出现,比如上面的三个。花点时间看看它们是否能在 2021 年改善你的企业 Java 应用。


via: https://opensource.com/article/21/3/enterprise-java-tools

作者:Daniel Oh 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

“微服务架构风格是一种将 单个应用程序 开发为一套 小型服务 的方法,每个服务都在 自己的进程中运行,并使用轻量级的通信机制(通常是 HTTP 类型的 API)进行通信。这些服务是围绕 业务能力 构建的,并且可以通过 全自动化的部署机制 进行 独立部署。目前对这些服务几乎没有集中的管理,这些服务可以用 不同的编程语言 编写,也能使用 不同的数据存储技术。”

—— James Lewis 和 Martin Fowler (2014) [1]

介绍

预计在 2020 年,全球云端的微服务市场将以 22.5% 的速度增长,其中美国市场预计将保持 27.4% 的增长率 [2] 。目前的趋势是,开发人员将从本地托管的应用程序转移到云端。这将有助于企业最大限度地减少停机时间、优化资源并降低基础设施成本。同时专家们还预测,到了 2022 年,90% 的应用程序将会使用微服务架构进行开发 [2:1] 。本文将帮助你了解什么是微服务,以及目前的公司如何使用它的。

什么是微服务?

微服务已经在全球范围内被广泛使用。但是,微服务到底是什么?微服务是一种基于许多小型、互联服务的体系结构模式。它们基于 单一责任原则。根据 Robert C. Martin 的说法,“将因相同原因而变化的事物聚集起来,将因不同原因而变化的事物分离开来”。 [3] 微服务架构也被扩展到了 松耦合服务 中,可以 独立地开发、部署和维护 [3:1]

远离单体架构

微服务通常和传统的单体软件架构做对比。在单体架构中,软件是被设计为自足的,也就是说,这个程序中的各个组件都是互相连通和互相依赖的,而不是松散耦合的。在一个紧耦合的架构中( 单体 monolithic ),每个组件和它相关联的组件必须按照指定的顺序组合起来,才能被执行或编译 [4] 。当其中有一个组件需要更新时,整个应用都要被重写。

而这个现象在使用微服务架构的应用中就不会出现。因为每一个模块都是独立的,每个模块都可以更新修改而不影响程序的其他部分。因此,降低了对更改一个组件会对其他组件造成影响的风险。

如果公司的架构很难升级,或者维护过于复杂和昂贵,那么他们可能会遇到麻烦,不能扩展单体架构的应用 [5] 。把一个复杂的任务分解成小组件,彼此独立工作,就是解决这个问题的方法。

单一体系架构 vs. 微服务架构 (图片来自 [6]

开发者如何构建属于自己的微服务

微服务以提高可扩展性性能而闻名。然而,这些是世界各地的开发者开发属于他们自己的微服务的主要原因吗?《微服务 2020 研究现状》 [7] 披露了全球开发者如何构建他们的微服务,以及他们对微服务的看法。这份报告是在来自欧洲、北美、中南美洲、中东、东南亚、澳大利亚和新西兰的 660 名微服务专家的帮助下完成的。下表列出了微服务成熟度相关问题的平均评分 [7:1]

分类平均得分(满分为5分)
创建新项目3.8
维护与调试3.4
工作效率3.9
解决可扩展性问题4.3
解决性能问题3.9
团队合作3.9

从上表可知,大部分专家都对使用微服务来解决可扩展性问题感到满意。与之相反的是,维护与调试对他们来说似乎是一个挑战。

从他们所使用的架构技术来说,大部分专家使用 Javascript/Typescript (大约 ⅔ 的微服务是使用这些语言构建的),其次使用的是 Java。

尽管有很多部署微服务的选择,但大多数专家使用 AWS(49%),其次是他们自己的服务器。另外,有 62% 的人更喜欢用 AWS Lambda 作为无服务器解决方案。

这些人所使用的大多数微服务都使用 HTTP 进行通信,其次是 events 和 gRPC。此外,大多数专家将 RabbitMQ 用于消息代理,其次是 Kafka 和 Redis。

而且,大多数人使用微服务持续集成(CI)。在报告中,87% 的受访者使用诸如 GitLab CI、Jenkins 或 GitHub Actions 等 CI 解决方案。

在 86% 的受访者中,最受欢迎的调试解决方案是日志,其中 27% 的受访者使用日志。

最后,大多数人认为微服务架构将成为更复杂的系统或后端开发的标准。

微服务的成功案例

许多公司已经从单体架构转向微服务架构。

亚马逊

在 2001 年,开发延迟、编码挑战和服务相互依赖性使得 亚马逊 Amazon 无法满足其不断增长的用户群的可扩展性需求。由于需要从头开始重构他们的单体架构,亚马逊将其单体架构应用程序拆分为小型的、独立的、针对服务的应用程序 [6:1] [8]

2001 年,在微服务这个词开始流行之前的几年,亚马逊决定改用微服务。这一变化使得亚马逊开发了好几种支持微服务架构的解决方案,比如亚马逊 AWS。随着对微服务的快速增长和适应,亚马逊成为全球市值最高的公司,截至 2020 年 7 月 1 日,亚马逊市值为 1.433 万亿美元 [9]

奈飞

奈飞 Netflix 于 2007 年开始提供电影流媒体服务,到了 2008 年,它也面临着规模扩张的挑战。期间,他们经历了一次严重的数据库损坏,在三天之内,他们不能将 DVD 发送给他们的会员 [10] 。这一事故使他们意识到需要将单点故障(如关系数据库)转向云中更可伸缩和更可靠的分布式系统。于是 2009 年,奈飞开始将其单体架构的应用重构为微服务。他们首先将其非面向客户的电影编码平台迁移到云端作为独立的微服务运行 [11] 。在改用微服务之后,使奈飞能够解决扩展性挑战和服务中断的问题。并且它还允许他们按照每个流数据而不是数据中心的成本来降低成本 [10:1] 。今天,奈飞每天向 190 个国家的 1.39 亿订户发送约 2.5 亿小时的内容 [11:1]

Uber

在推出 Uber 服务之后,他们在开发和发布新功能、修复 bug,以及迅速整合新的变化方面遇到了困难。因此,他们决定改用微服务,并将应用程序结构拆分为基于云的微服务。换句话说,Uber 为每个功能创建了一个微服务,比如乘客管理和出行管理。转向微服务给 Uber 带来了很多好处,比如对每项服务的所有权都有一个清晰的概念。这提高了服务访问的速度和质量,通过允许团队只关注他们需要扩展的服务,在更新虚拟服务的同时而不中断其他服务,实现了更可靠的容错,从而促进了快速扩展 [11:2]

这就是可扩展性!

关于如何提供可伸缩性的一个很好的例子是看看中国。中国人口众多,必须通过创造和试验新的解决方案来适应规模化的新挑战。统计数据显示,中国目前为大约 9 亿互联网用户提供服务 [12] 。2019 年“双十一”期间(相当于国外的黑色星期五),阿里巴巴旗下各购物平台的交易峰值为每秒 544000 笔交易。阿里云处理的数据总量约为 970 PB [13] 。那么,这些数量的用户在技术上意味着什么呢?

为了解决可伸缩性问题,许多技术应运而生。例如,Tars 由腾讯于 2008 年创建,2018 年贡献给 Linux 基金会。它也在被大规模使用,并在 10 年内得到了很大的提升 [14] 。TARS 是开源的,许多组织都在大力贡献和扩展框架的特性和价值 [14:1] 。TARS 支持多种编程语言,包括 C++、Golang、java、node.js、PHP 和 Python;它可以快速构建系统并自动生成代码,使开发人员能够专注于业务逻辑,从而有效地提高操作效率。TARS 已广泛应用于腾讯的 QQ、微信社交网络、金融服务、边缘计算、汽车、视频、网络游戏、地图、应用市场、安全等诸多核心业务。在 2020 三月,TARS 项目转变为 TARS 基金会,这是一个开源微服务基金会,在建立开放式微服务平台的社区方面中,致力于提升社区贡献和成员的快速增长 [14:2]

一定要看看 Linux 基金会新的免费培训课程:《用 TARS 构建微服务平台

关于作者:

Isabella Ferreira 是 Linux 基金会旗下的开源微服务基金会 TARS 基金会的布道师

Mark Shan(单致豪)是腾讯开源联盟的主席,也是 TARS 基金会的董事会主席。

本篇 Linux 基金会白金赞助商内容由腾讯贡献。

  1. https://martinfowler.com/articles/microservices.html#footnote-etymology ↩︎
  2. https://www.charterglobal.com/five-microservices-trends-in-2020/ ↩︎ ↩︎
  3. https://medium.com/hashmapinc/the-what-why-and-how-of-a-microservices-architecture-4179579423a9 ↩︎ ↩︎
  4. https://whatis.techtarget.com/definition/monolithic-architecture ↩︎
  5. https://www.leanix.net/en/blog/a-brief-history-of-microservices ↩︎
  6. https://www.plutora.com/blog/understanding-microservices ↩︎ ↩︎
  7. https://tsh.io/state-of-microservices/#ebook ↩︎ ↩︎
  8. https://thenewstack.io/led-amazon-microservices-architecture/ ↩︎
  9. https://ycharts.com/companies/AMZN/market_cap ↩︎
  10. https://media.netflix.com/en/company-blog/completing-the-netflix-cloud-migration ↩︎ ↩︎
  11. https://blog.dreamfactory.com/microservices-examples/ ↩︎ ↩︎ ↩︎
  12. https://www.statista.com/statistics/265140/number-of-internet-users-in-china/ ↩︎
  13. https://interconnected.blog/china-scale-technology-sandbox/ ↩︎
  14. https://www.linuxfoundation.org/blog/2020/03/the-tars-foundation-the-formation-of-a-microservices-ecosystem/ ↩︎ ↩︎ ↩︎

via: https://www.linux.com/news/the-state-of-the-art-of-microservices-in-2020/

作者:Linux.com 选题:lujun9972 译者:zhangxiangping 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在问题导致关键的微服务瘫痪之前,使用 GraphQL 的监控功能帮助你及早发现问题。

微服务GraphQL 就像面包和黄油一样,是一个很好的组合。它们本身都很棒,结合起来就更棒了。了解你的微服务的健康状况是很重要的,因为它们运行着重要的服务。如果等到某个关键的服务崩溃了才诊断问题,那是很愚蠢的。让 GraphQL 帮助你及早发现问题并不需要花费太多精力。

 title=

常规的健康检查可以让你观察和测试你的服务,在问题影响到你的业务、客户或项目之前,尽早得到通知。说起来很简单,但健康检查到底要做什么呢?

以下是我在设计服务检查时考虑的因素:

服务器健康检查的要求:

  1. 我需要了解我的微服务的可用性状态。
  2. 我希望能够管理服务器的负载。
  3. 我希望对我的微服务进行端到端(e2e)测试。
  4. 我应该能够预测中断。

 title=

做服务器健康检查的方法

进行健康检查可能比较棘手,因为理论上,你可以检查的东西几乎是无穷无尽的。我喜欢从小处着手,运行最基本的测试:ping 测试。这只是测试运行应用的服务器是否可用。然后,我加强测试以评估特定问题,思考服务器中最重要的元素。我想到那些如果突然消失的话将是灾难性的事情。

  1. **Ping 检查:**Ping 是最简单的监控类型。它只是检查你的应用是否在线。
  2. **脚本化浏览器:**脚本化浏览器比较高级。像 Selenium 这样的浏览器自动化工具可以让你实现自定义的监控规则集。
  3. **API 测试:**API 测试用于监控 API 端点。这是 ping 检查模型的高级版本,你可以根据 API 响应来定义监控计划。

使用 GraphQL 进行健康检查

在一个典型的基于 REST 的微服务中,你需要从头开始构建健康检查功能。这是一个时间密集型的过程,但使用 GraphQL 就不用担心了。

根据它的网站称:

“GraphQL 是一种用于 API 的查询语言,也是一种用现有数据完成这些查询的运行时环境。GraphQL 为你的 API 中的数据提供了一个完整的、可理解的描述,让客户有能力精确地仅查询他们所需要的东西,让 API 更容易随着时间的推移而进化,并实现强大的开发者工具。”

当你启动一个 GraphQL 微服务时,你还可以获得监控微服务的运行状况的供给。这是一个隐藏的宝贝。

正如我上面提到的,你可以用 GraphQL 端点执行 API 测试以及 ping 检查。

Apollo GraphQL 服务器提供了一个默认的端点,它可以返回有关你的微服务和服务器健康的信息。它不是很复杂:如果服务器正在运行,它就会返回状态码 200。

默认端点是 <server-host>/.well-known/apollo/server-health

 title=

高级健康检查

在某些情况下,基本的健康检查可能不足以确保系统的完整性。例如,紧密耦合的系统需要更多的业务逻辑来确保系统的健康。

Apollo GraphQL 在定义服务器的同时,通过声明一个 onHealthCheck 函数来有效地管理这种情况。

* Defining the Apollo Server */
const apollo = new ApolloServer({
  playground: process.env.NODE_ENV !== 'production',
  typeDefs: gqlSchema,
  resolvers: resolver,
  onHealthCheck: () => {
    return new Promise((resolve, reject) => {
      // Replace the `true` in this conditional with more specific checks!
      if (true) {
        resolve();
      } else {
        reject();
      }
    });
  }
});

当你定义一个 onHealthCheck 方法时,它返回一个 promise,如果服务器准备好了,它就会返回 resolve,如果有错误,它就会返回 reject

GraphQL 让监控 API 变得更容易。此外,在你的服务器基础架构中使用它可以使代码变得可扩展。如果你想尝试采用 GraphQL 作为你的新基础设施定义,请参见我的 GitHub 仓库中的示例代码和配置


via: https://opensource.com/article/20/8/microservices-graphql

作者:Rigin Oommen 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出