Spring Cloud 微服务架构,小团队到底适不适合,如何应用?

本文探讨了从单体应用到微服务架构的转变,涉及数据库管理、CI/CD流程、SpringCloud在微服务中的应用、服务拆分原则、框架选择、Kubernetes的引入,以及链路跟踪和运维监控的升级。作者还分享了Java开发学习资源,特别是在面试准备方面的建议。
摘要由CSDN通过智能技术生成

这在开发初期为我们提供了非常大的便利性。但值得注意的是,由于数据库以及其他资源的引入,数据准备以及数据清理时要考虑的问题就会更多,例如如何控制并行任务之间的测试数据互不影响等等。

为了让这一套流程可以自动化的运作起来, 引入Jenkins也是理所当然的事情了。

开发人员提交代码进入gerrit中,Jenkins被触发开始编译代码并执行集成测试,完成后生成测试报告,测试通过再由reviewer进行代码review。在单体应用时代这样的CI架构已经足够好用,由于有集成测试的覆盖,在保持API兼容性的前提下进行代码重构都会变得更有信心。

微服务时代

服务拆分原则

从数据层面看,最简单的方式就是看数据库的表之间是否有比较少的关联。例如最容易分离的一般来说都是用户管理模块。如果从领域驱动设计(DDD)看,其实一个服务就是一个或几个相关联的领域模型,通过少量数据冗余划清服务边界。

单个服务内通过领域服务完成多个领域对象协作。当然DDD比较复杂,要求领域对象设计上是充血模型而非贫血模型。

从实践角度讲,充血模型对于大部分开发人员来说难度非常高,什么代码应该属于行为,什么属于领域服务,很多时候非常考验人员水平。

服务拆分是一个大工程,往往需要几个对业务以及数据最熟悉的人一起讨论,甚至要考虑到团队结构,最终的效果是服务边界清晰, 没有环形依赖和避免双向依赖。

框架选择

由于之前的单体服务使用的是spring boot,所以框架自然而的选择了spring cloud。其实个人认为微服务框架不应该限制技术与语言,但生产实践中发现无论dubbo还是spring cloud都具有侵入性,我们在将nodejs应用融入spring cloud体系时就发现了许多问题。也许未来的service mesh才是更合理的发展道路。

这是典型的Spring Cloud的使用方法

  • zuul作为gateway,分发不同客户端的请求到具体service

  • erueka作为注册中心,完成了服务发现和服务注册

  • 每个service包括gateway都自带了Hystrix提供的限流和熔断功能

  • service之间通过feign和ribbon互相调用,feign实际上是屏蔽了service对erueka的操作

上文说的一旦要融入异构语言的service,那么服务注册,服务发现,服务调用,熔断和限流都需要自己处理。

再有关于zuul要多说几句,Sprin Cloud提供的zuul对Netflix版本的做了裁剪,去掉了动态路由功能(Groovy实现),另外一点就是zuul的性能一般,由于采用同步编程模型,对于IO密集型等后台处理时间长的链路非常容易将servlet的线程池占满,所以如果将zuul与主要service放置在同一台物理机上,在流量大的情况下,zuul的资源消耗非常大。

实际测试也发现经过zuul与直接调用service的性能损失在30%左右,并发压力大时更为明显。现在spring cloud gateway是pivotal的主推了,支持异步编程模型,后续架构优化也许会采用,或是直接使用Kong这种基于nginx的网关来提供性能。当然同步模型也有优点,编码更简单,后文将会提到使用ThreadLocal如何建立链路跟踪。

架构改造

经过大半年的改造以及新需求的加入,单体服务被不断拆分,最终形成了10余个微服务,并且搭建了Spark用于BI。初步形成两大体系,微服务架构的在线业务系统(OLTP) + Spark大数据分析系统(OLAP)。数据源从只有Mysql增加到了ES和Hive。多数据源之间的数据同步也是值得一说的话题,但内容太多不在此文赘述。

自动化部署

与CI比起来,持续交付(CD)实现更为复杂,在资源不足的情况我们尚未实现CD,只是实现执行了自动化部署。

由于生产环境需要通过跳板机操作,所以我们通过Jenkins生成jar包传输到跳板机,之后再通过Ansible部署到集群。

简单粗暴的部署方式在小规模团队开发时还是够用的,只是需要在部署前保证测试(人工测试 + 自动化测试)到位。

链路跟踪

开源的全链路跟踪很多,比如spring cloud sleuth + zipkin,国内有美团的CAT等等。其目的就是当一个请求经过多个服务时,可以通过一个固定值获取整条请求链路的行为日志,基于此可以再进行耗时分析等,衍生出一些性能诊断的功能。不过对于我们而言,首要目的就是trouble shooting,出了问题需要快速定位异常出现在什么服务,整个请求的链路是怎样的。

为了让解决方案轻量,我们在日志中打印RequestId以及TraceId来标记链路。RequestId在gateway生成表示唯一一次请求,TraceId相当于二级路径,一开始与RequestId一样,但进入线程池或者消息队列后,TraceId会增加标记来标识唯一条路径。

举个例子,当一次请求会向MQ发送一个消息,那么这个消息可能会被多个消费者消费,此时每个消费线程都会自己生成一个TraceId来标记消费链路。加入TraceId的目的就是为了避免只用RequestId过滤出太多日志。实现如图所示,

简单的说,通过ThreadLocal存放APIRequestContext串联单服务内的所有调用,当跨服务调用时,将APIRequestContext信息转化为Http Header,被调用方获取到Http Header后再次构建APIRequestContext放入ThreadLocal,重复循环保证RequestId和TraceId不丢失即可。如果进入MQ,那么APIRequestContext信息转化为Message Header即可(基于Rabbitmq实现)。

当日志汇总到日志系统后,如果出现问题,只需要捕获发生异常的RequestId或是TraceId即可进行问题定位

运维监控

在容器化之前,采用telegraf + influxdb + grafana的方案。telegraf作为探针收集jvm,system,mysql等资源的信息,写入influxdb,最终通过grafana做数据可视化。spring boot actuator可以配合jolokia暴露jvm的endpoint。整个方案零编码,只需要花时间配置。

容器化时代

架构改造

因为在做微服务之初就计划了容器化,所以架构并未大动,只是每个服务都会建立一个Dockerfile用于创建docker image

涉及变化的部分包括:

  1. CI中多了构建docker image的步骤

  2. 自动化测试过程中将数据库升级从应用中剥离单独做成docker image

  3. 生产中用k8s自带的service替代了eruka

理由下文一一道来。

Spring Cloud与k8s的融合

我们使用的是Redhat的Openshift,可以认为是k8s企业版,其本身就有service的概念。一个service下有多个pod,pod内即是一个可服务单元。service之间互相调用时k8s会提供默认的负载均衡控制,发起调用方只需要写被调用方的serviceId即可。这一点和spring cloud fegin使用ribbon提供的功能如出一辙。

也就是说服务治理可以通过k8s来解决,那么为什么要替换呢?其实上文提到了,Spring Cloud技术栈对于异构语言的支持问题,我们有许多BFF(Backend for Frontend)是使用nodejs实现的,这些服务要想融合到Spring Cloud中,服务注册,负载均衡,心跳检查等等都要自己实现。

如果以后还有其他语言架构的服务加入进来,这些轮子又要重造。基于此类原因综合考量后,决定采用Openshift所提供的网络能力替换eruka。

由于本地开发和联调过程中依然依赖eruka,所以只在生产上通过配置参数来控制,

eureka.client.enabled` 设置为 false,停止各服务的eureka注册

ribbon.eureka.enabled 设置为 false,让ribbon不从eureka获取服务列表

以服务foo为例,foo.ribbon.listofservers 设置为 http://foo:8080,那么当一个服务需要使用服务foo的时候,就会直接调用到`http://foo:8080

CI的改造

CI的改造主要是多了一部编译docker image并打包到Harbor的过程,部署时会直接从Harbor拉取镜像。另一个就是数据库的升级工具。之前我们使用flyway作为数据库升级工具,当应用启动时自动执行SQL脚本。

随着服务实例越来越多,一个服务的多个实例同时升级的情况也时有发生,虽然flyway是通过数据库锁实现了升级过程不会有并发,但会导致被锁服务启动时间变长的问题。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

感受:

其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。

特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

拿到字节跳动offer后,简历被阿里捞了起来,二面迎来了P9"盘问"

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

[外链图片转存中…(img-nXObjYWI-1712194803859)]

《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 28
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值