抗战电影中出场率很高的边三轮竟然在软件界能混的风生水起!

221 阅读11分钟

前言

笔者长期关注大数据通用技术,通用原理以及NOSQL数据库的技术架构以及使用,云原生相关的技术以及使用。欢迎大家关注,在大数据的路上一起打怪升级!微信公众号搜索:“克己守心”即可,头像和掘进头像相同!

如果大家经常看抗战相关的电影或者电视剧,一定会对里面出场率很高的边三轮记忆犹新,这种绿色的三轮摩托几乎成了“太君”的代名词。而且战斗打响后,这个边三轮肯定第一时间被打爆,要么是被地雷炸飞,要么是被手榴弹送回老家...

影视作品中的边三轮虽然命运都很悲惨,但是在现实世界里却受到很多国家军队的喜欢。不只是日军装备了大量边三轮,二战中引领潮流的德军更是对边三轮偏爱有加,甚至成为了德军摩托化步兵师的标配。这些一个驾驶员配一个机枪手或者指挥官的小摩托频繁出现在二战初期德军闪电战的场景中,见证了二战初期德军的辉煌。

二战已经过去了许多年,长江后浪推前浪,边三轮已经慢慢的淡出了历史舞台,取而代之的是各种多功能的步兵车。就在大家都开始慢慢淡忘它的时候,他却以另外一种方式重新出现在了软件从业人员的视野中...

软件界的边三轮——边车模式(sidecar)

长期以来,软件业中的编程都流传着高内聚低耦合的设计规范,这也事实上成为判断软件设计尤其是面对对象设计好坏的标准。

而高内聚低耦合主要看类的内聚性是否高,耦合度是否低。目的是增强程序模块的可重用性、可移植性。通常程序结构中各模块的内聚程度越高,模块间的耦合程度就越低。

内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事,它描述的是模块内的功能联系;耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。

云原生时代带来了新的模式和挑战,云原生要求的容器化、服务网格、微服务以及不可变的基础设施在方方面面都穿插着高内聚低耦合的思想。那么这些功能怎么实现呢?其中的一大功臣边车模式诞生了,那个一个驾驶员驮着另一个功能人员的小摩托以另一种方式重新出现在大家的视野中。

边车模式的使用场景

边车模式是一种分布式架构的设计模式。边车就是加装在摩托车旁来达到拓展功能的目的,比如行驶更加稳定,可以拉更多的人和货物等。而坐在边车上的人可以根据具体需求来安排,比如可以放一个机枪手来提供火力支援,也可以放一个指挥官来进行军事指挥。如果平时没需求,摩托车也可以不加装边车单独行驶。由此看来,边三轮的灵活性以及实用性相当之高。

回到软件的世界,边车模式通过给应用服务加装一个“边车”来达到控制和逻辑的分离的目的。比如日志记录、监控、流量控制、服务注册、服务发现、服务限流、服务熔断等在业务服务中不需要实现的控制面功能,可以交给“边车”,业务服务只需要专注实现业务逻辑即可。这与分布式和微服务架构完美契合,真正的实现了控制和逻辑的分离与解耦。

说完了理论,我们再通过一个例子来看一下边车模式是如何发展以及工作的。如果想对一个应用服务进行日志监控,你会怎么做呢?

功能耦合

如果是初学者接收这个方案,可能直接都将逻辑实现写在一起。业务逻辑一个线程,日志收集一个线程,服务监控一个线程。如果日志收集和服务监控简单的话,还可以直接使用log4j等三方实现,这种场景下各个线程做各自的事情,相互配合,是不是很完美?

虽然看起来很丰满,但是现实却很骨感。试想如果日志收集和服务监控业务逻辑比较复杂,且需求变化频繁,这种情况该怎么破?如果在别的业务系统也需要日志收集以及服务监控,那么逻辑差不多的代码是不是需要重新实现一次?

上面的实现显然违反了高内聚低耦合的设计理念,带来了繁重的代码版本维护以及大量的代码冗余。开发人员只能通过频繁的代码修改来维护不断变更和扩展的用户需求,这显然不是很好的解决问题的方式。

初代边车

上面那个方案的失败之后,我们再次回到高内聚低耦合的路子上。这次我们使用单独的进程实现业务逻辑,以进程的方式单独交付,而日志收集以及监控服务等也以单独的进程提供服务,这就是初代边车模式。这种做法弥补了上面的问题,使得代码和业务的维护变得简单,并且使得相同功能的代码和逻辑可以复用,增加了开发效率。

虽然这个方案相对上面的方案有了很大的进步,但是由于进程间的接口对接以及各个进程的各种配置相对来说比较复杂且容易出错,如果每部署一次就要搞一回的话,整个的工作量还是挺大的。

在docker等容器出现后,可以通过容器编排进一步增加整个过程的效率。将业务系统和外围系统通过容器编排打包成一个容器来管理。这样既避免了每次部署的重复配置工作,又进一步增强了不可变性。拥有了容器的加持,我们就能够靠约定来简化打包和发布操作。看起来很完美,没问题了吧?

别高兴太早了,由于docker镜像分层的特点,如果docker镜像中的一层由于业务逻辑变更而发生改变,那么这层以及其外层的所有镜像都需要重新打包,这显然不够优雅。那有没有更好的方式了呢?

高级边车

K8S的出现终于彻底解决了该问题。K8S中的最小调度单位pod中可以包含多个container,这个机制使得边车模式又有了用武之地。借助于K8S的容器编排,只需要将业务系统和外围系统作成基础的镜像,再通过yaml文件将这些镜像管理起来,通过pod这个载体共同运行在同一个“地方”,即达到了之前预期的效果。相对于docker镜像的富容器模式,在K8S中只需要修改yaml的编排方式即可以完成适配需求变更的场景。

如上图所示,pod中的container0的业务系统就像边三轮的驾驶员负责开车,而container1和container2则像是工具人,根据需求可以是日志收集,服务监控,服务发现等功能,搭上便车做好自己的事情即可。

下面给个k8s上deployment使用beat采集日志的例子来说明下问题:

将bangcle-transfer和bangcle-filebeat放在同一个pod中,通过边车模式和共享存储logs进行日志数据的传输,从而达到k8s环境中日志收集的需求。

边车模式的里程碑——ServiceMesh

上面的场景只是边车模式小试牛刀,边车模式的高光时刻则是随着云原生中的ServiceMesh一同到来。

在软件的世界里,如果是小业务量,怎么玩就可以,但是如果数据量到达了一定的规模,那就是考验真正的实力了。试想,如果你的系统有100个服务需要管理,这些服务需要有一个统一管理的接口且各个服务之间还有相关的数据或者操作接口,那么怎么破?

常规的服务管理方式可能会让运维人员崩溃,而ServiceMesh的出现则完美的解决了这个问题:

  • 每个服务都有一个sidecar模式的服务管理进程进行管理
  • 所有服务间的通信与数据交换都是通过sidecar进程来展开的

  • 当把业务服务刨除后,剩下的就是side模式进程的关系网,每个进程之间的实线连线代表着数据或者指令的流动
  • 虚线则代表着每个sidecar进程产生的数据以及指令的汇总,这些指令汇总到控制平面进行统一管理

综上,ServiceMesh从原理上实现了服务治理相关的工作。例如当前比较流行的Istio,通过负载均衡、服务间的身份验证、监控等方法,它可以轻松地创建一个已经部署了服务的网络,而服务的代码只需很少更改甚至无需更改。通过在整个环境中部署一个特殊的 sidecar代理,为服务添加 Istio 的支持,而代理会拦截微服务之间的所有网络通信,然后使用其控制平面的功能来配置和管理Istio。

从上面可以看出,ServiceMesh将业务属性剥离了出去,只剩下一张控制属性的大网,涵盖了所有运维和基础服务的工作,从而实现对于复杂的大数据量的服务的统一管理。

最后看看官方的功能描述:

  • 为 HTTP、gRPC、WebSocket 和 TCP 流量自动负载均衡。
  • 通过丰富的路由规则、重试、故障转移和故障注入对流量行为进行细粒度控制。
  • 可插拔的策略层和配置 API,支持访问控制、速率限制和配额。
  • 集群内(包括集群的入口和出口)所有流量的自动化度量、日志记录和追踪。
  • 在具有强大的基于身份验证和授权的集群中实现安全的服务间通信。

但是ServiceMesh的实现方式也注定有其缺点:

  • 由于都是通过sidecar模式进行访问和数据传输,导致数据传输效率下降,排查问题变得麻烦,时间成本上升
  • 为了适配sidecar模式的服务管理,必要的代理以及适配器还是必须的,这部分的工作量是无法忽略的

但是瑕不掩瑜,为了达到更好的性能和效果,付出这点代价还是值当的,尤其是大规模大数据量的服务系统。

总结

从上文可以看出,边车模式其实就是在业务系统这个驾驶员实现业务逻辑的基础上,加了个边车,然后根据需求搭配不同的控制逻辑,从而达到这个业务实现以及服务治理相结合的方式。

这样做的好处是在开发以及运维人员之间做了一个很优雅的任务划分,开发人员集中精力完成业务逻辑的开发,运维人员集中精力完成整个的服务治理即可,两方之间只要接口不变,其余都可以做双方无感知的升级与修改,提供了强大的兼容性和扩展性,真正做到研发与运维解耦合,各司其职。

时代在发展,技术在进步,各种技术和组件层出不穷,但是思想的变更却没有这么频繁,尤其是那些经典的编程思想以及设计模式。而真正决定一种设计模式生命力的往往是灵活性以及扩展性,毕竟要接受现代化的改造或者适配还是需要一定的兼容性。武器装备如此,设计模式亦是如此!

文章到这里就结束了,如果想一起入门学习大数据以及云原生的小伙伴,欢迎点赞转发加关注,下次学习不迷路!