使用服务网格/Istio开发微服务1:背景及开发约定

2020-05-25 10:07:51 浏览数 (1)

背景

微服务会把大的应用拆分成若干小的服务应用和前端应用,如何协调/治理这些应用,并解决在开发中遇到的各种问题是微服务面临的挑战。通常一个微服务系统需要关注的问题有:

  • 服务的注册发现
  • 服务间的远程调用
  • 负载均衡/东西向流量操控
  • 网关/南北向流量操控
  • 弹性伸缩
  • 服务的调用链跟踪
  • 日志收集和告警
  • 熔断和限流
  • 远程配置管理
  • 健康检测
  • 故障恢复
  • 服务自动漂移 等等

为了解决上述问题,诞生了象 Spring Cloud 这样伟大的开发框架和系统。但这些能力都分布在各种 SDK 中,需要编写特定的代码进行操控,业务开发者需要理解整个框架,并熟悉周边的辅助系统。这些,无疑对开发者造成了困扰,提高了入门门槛。这个也造成了很多企业对于微服务改造的恐惧。

服务网格恰恰屏蔽了这些内容,将服务治理完全下沉到网络层。开发者无需再编写与业务无关的代码了。

“我只要写好 restful 的服务,丢到服务网格体系中,就 run 起来了,上面的那些能力自动获得。”

一个典型的服务网格应用示意如下图:

istio 架构istio 架构

开发框架和组织的解耦

微服务通常由多个小组协同开发。在不了解服务网格的情况下,一般的企业技术决策者对于研发部门的能力考虑是单一技术栈,单一框架,考虑的出发点是技术栈可以保持延续,主要管理层可控(采用自己或心腹较擅长的技术)。并且如果企业大规模采用传统微服务开发,深入理解这些框架的大牛变得更加重要,一旦离开,后果便是灾难性的(如人人视频的 dubbo 大牛的离开)。

但在服务网格体系中,完全摆脱了技术栈和微服务框架的约束,允许开发者使用自己擅长的技术栈。企业决策者再也不必被某个核心技术人员掣肘,最难的服务治理问题已经被解决掉了。

服务网格对微服务的技术人员的要求和分工也带来了一些变化。通常的需求有两类:

  • 业务(服务)开发者:理解企业业务,并具有应用开发能力。按照上面的架构图,业务开发者写好 Service A 和 Service B 就好了。这部分角色的需求没有变化,本来在企业中就存在。
  • 运维开发:只需要理解容器,容器编排,以及服务网格/service mesh/isito,写一些部署脚本,或者把当前的 CI/CD 系统连接到服务网格就好了。这部分是需要新学习的内容,好在他足够简单,并且有云厂商的“贴身服务”。

当然,只要愿意,这两种角色可以合并,每个开发者都可以成为“技术大牛”。

但协调各个应用,仍然会需要约定,要求业务开发小组共同遵循。这些约定应该是适用所有微服务开发技术。

服务响应格式统一

在服务网格中,服务端一般采用 http 的 restful 的方式。统一的响应格式会为开发带来便利,也易于封装统一的调用。

下面是一个典型的响应封装:

代码语言:txt复制
{
    "service": "passport",
    "timeStamp": 1559360704569,
    "success": true,
    "data": {
        "items": [
            {
                "id": 1,
                "title": "腾讯云",
            }
        ],
        "total": 1,
        "limit": 20,
        "offset": 0
    }
}

当然,为了通信效率,我们在服务之间调用可以使用其他的二进制 rpc 协议。在 istio 体系中,默认支持了谷歌自家的 gRPC。通过对 envoy filter 的扩展,还会支持更多的 RPC 协议,如 thift,dubbo 等。

远程访问的约定

通常对于OO类的语言,会屏蔽接口格式和通信协议,把这些约定写入开发框架或者 SDK 中,远程调用就像本地调用一样(如 java 的 feign),这样的好处很明显。但缺点也很明显:你被框架/SDK 绑定了。

服务网格中,Restful 服务间的调用采用的是 协议 (http or https) 内部服务名/域名 端口 调用,如:http://passport.xyz.svc.cluster.local:7301/{your_api_url} 。在这里的一个较好的实践是:在配置文件中使用短服务名映射,屏蔽 http 协议 和 端口,并在部署的时候将这个配置放到远程配置中心。如:

代码语言:txt复制
{
  "passport":"https://passport.xyz:7301/"
}

在应用中:

代码语言:txt复制
//屏蔽协议,端口和域名
private String getRemote(String service, String url){
  String uri = Util.getServiceUrl(service)   url;
  return restTemplate.getForObject(uri, String.class);
}
代码语言:txt复制
//调用上述方法
String result = getRemote("passport", "/open/account/info?ticket=xxx");

这样就可以屏蔽由于部署导致的协议,端口,和域名的变更。

服务的拆分和暴露

应用拆分的颗粒度问题:

  • 原子服务:基础的服务,与领域模型对应,直接与数据库打交道,包含增删改查等操作,此类服务只对内暴露。
  • 业务服务:对应前端具体的业务,具有业务逻辑,有可能与数据库打交道,也可能访问其他原子服务或业务服务。有些集成了安全认证的服务可以对外暴露。
  • 业务应用:直接面对最终用户的应用,可能包含UI,也可能是开放的API,必须包含安全认证。
  • 网关:通常会作为内部服务对外的出口,有安全认证,编排或协议转换等功能。

服务的拆分没有一定的规则,不同的架构师/开发者会有不同的拆分方法。

典型应用架构典型应用架构

服务暴露通常会带来安全的挑战。

通常建议最小暴露,按需暴露。

0 人点赞