Spring Cloud 微服务开发实战

为什么要学习微服务?

1. 求职时增强技术自信。 微服务是非常热门的话题,企业招聘中也越来越多的要求有微服务开发、架构能力,不掌握一些微服务技能找工作时也缺乏亮点,可以说微服务架构已经成为中高级后端开发人员、架构师的必备技能。

2. 提升技术实力,增加职业转型的可能性。 长期从事局部功能开发,会导致全局掌控能力缺失。软件系统是一个复杂工程,只有从更高的角度统观全局,考虑业务的方方面面以及未来可能的演进方向,才能深刻理解一个产品或项目的内在含义,而这个话语权往往掌握在更高职级的开发者、设计师、架构师手中,如果掌握了一套微服务架构、开发理念,增加了向更高职级晋升的可能性。

3. 解决工作中软件研发难题。 随着软件复杂度的提升,社会分工的越来越细,单体应用或者粗放式的软件服务,大量功能代码堆积在一起,显得特别臃肿繁杂,开发维护成本很高。这在日常运维、升级维护时非常不便,一个小功能的变更都有可能导致整个工程出现问题甚至宕机,如果是运行中的生产环境崩溃,由此所造成的经济损失或不好的社会影响,将是不可估量的。而引入微服务,可以更好的解决这一系列的问题。

4. 保持技术的前瞻性。 研发技术迭代日新月异,新概念新应用也是层出不穷,云原生架构、容器化运维、中台等等,都与微服务有着微妙的关系,只有保持技术的持续性,才能更好的学习新技术,否则会很不利于新技术的落地应用。

微服务有什么用?能解决实际业务场景中哪些问题?

将单体应用分拆成多个小体量的的高内聚低耦合的软件服务,可以采用不同的软件语言,不同的数据存储,每个小服务独立部署运行,服务间采用轻量级的通信机制,由不同的团队开发和维护,大大提高了软件的可维护性、扩展性。

采用微服务架构,可以帮助我们很好的解决实际业务场景中的问题:

  • 从组织层面讲,系统的分工更加明确,责任更加清晰。

  • 按照业务来划分服务,单个服务代码量小,业务单一,更易于维护。

  • 服务能力更专一,代码重复度降低,可复用性高,使服务调用更加简便。

  • 服务之间耦合性低,可随时加入服务,剔除过时服务。

  • 单个服务也可以弹性扩展,容错能力更强,

  • 更贴合云时代背景的软件开发运维环境,高效自动化构建部署。

  • 数据存储解耦,不必将所有数据集中在一个大库中,不易拆分扩容。

为什么选择 Spring Cloud?

Spring Cloud 的核心特性如下Spring Cloud 优点多多,从如下几点可以看出:

  • 源于 Spring,社区活跃、生态丰富、功能稳定,资料多,遇到问题很容易找到解决方案

  • 基于 Spring Boot,组件丰富,开箱即用,更便于业务落地。

  • 相比于其它框架,Spring Cloud 对微服务周边环境的支持力度更大。

  • 开发人员从业基数庞大,对于中小企业来讲,使用门槛较低。

  • 相当一部分企业产品实践,已经在使用 Spring Cloud 作为技术选型来落地应用,支撑实际业务。

再来看看 Spring Cloud 的生态组件有哪些,如何帮助我们快速建立微服务架构体系。

Spring Cloud 以全家桶的形式出现,提出了一整套的微服务解决方案,基本囊括了微服务体系中全部组件应用。每个组件又基于 Spring Boot 技术体系,提供开箱即用、约定优于配置的开发方案,可以使开发者们绕过繁琐的组件整合、环境配置等等,快速上手微服务。并且经过了 Spring 社区的整合,有大量的兼容性测试作基础,Spring Cloud 的稳定性是有保证的,我们也不用费尽心思的自己去拼微服务的各个积木块,而且也不一定比它更优秀。

国内开发者在纠结 Dubbo 与 Spring Cloud 的使用问题,Spring Cloud 不少功能组件的停更,环境搭建繁杂,近年阿里推出的 Spring Cloud Alibaba 组件迎头而上,上手更简单,学习曲线低,可更好与 Spring Cloud 生态融合在一起,强强联合,更加增加了 Spring Cloud 的在 Java 领域微服务开发的优势地位。

选择 Spring Cloud,不论是从开发人员储备、社区活跃度,还是厂商友好性、生态稳定性等角度考虑,都是做微服务架构选型一个良好选择。

专栏内容

本专栏就带你一起,将 Spring Cloud 技术融入到一个实际项目——“商场停车”中,一步一步将技术点串联起来,服务注册、服务发现、服务接口管理、配置中心、分布式事务、统一网关、服务限流降级等,一个点一个点慢慢啃,由点成线,由线成面,等专栏结束再回顾,Spring Cloud 微服务开发原来也没有想象中的那么难。

主要内容分三个大模块:前期准备阶段,系统实现阶段以及高级应用部分。

  • 第一部分,准备篇,按照软件开发流程,进行早期的需求分析、系统设计、存储设计、架构设计工作,并搭建出架构雏形,介绍相关开发组件,为后期开发打好基础。

  • 第二部分,系统实现篇,结合实际业务功能一步一步完成微服务场景下所需的特性功能,包括服务的注册与发现、服务之间的调用、服务调用时快速失败/降级、配置中心、分布式缓存、面向端的服务适配、消息推送、网关路由、流量控制、统一鉴权等等。

  • 第三部分,开发进阶篇,在前期开发的基础上拔高,包括聚合 API 接口,对外提供统一入口;引入分布式事务,确保数据的完整性;加入分布式锁,防止限量产品出现超卖的情况;提供应用监控,实时观察各个系统应用实例的健康状态;引入链路追踪中间件,完整的串联系统的每次请求,监控系统调用的效率,为后期系统优化、服务可用性降级提供支撑依据。

专栏目录

  1. 开篇导读
  2. 以真实“商场停车”业务切入一需求分析
  3. 具象业务需求再抽象分解一系统设计
  4. 第一个 Spring Boot 子服务一会员服务
  5. 如何维护接口文档供外部调用一在线接口文档管理
  6. 认识 Spring Cloud 与 Spring Cloud Alibaba 项目
  7. 服务多不易管理如何破一服务注册与发现
  8. 如何调用本业务模块外的服务一服务调用
  9. 服务响应慢或服务不可用怎么办一快速失败与服务降级
  10. 热更新一样更新服务的参数配置一分布式配置中心
  11. 如何高效读取计费规则等热数据一分布式缓存
  12. 多实例”下的定时任务如何避免重复执行一分布式定时任务
  13. 同一套服务如何应对不同终端的需求一服务适配
  14. 采用消息驱动方式处理扣费通知一集成消息中间件
  15. Spring Cloud 与 Dubbo 冲突吗一强强联合
  16. 破解服务中共性问题的繁琐处理方式一接入 API 网关
  17. 服务压力大系统响应慢如何破一网关流量控制
  18. 集成网关后怎么做权限隔离一统一鉴权
  19. 如此多服务模块,接口如何管理一聚合 API
  20. 数据分库后如何确保数据完整性一分布式事务
  21. 优惠券如何避免超兑一引入分布式锁
  22. 如何查看各服务的健康状况一系统应用监控
  23. 如何确定一次完整的请求过程一服务链路跟踪
  24. 结语

多图文配套讲解

为了让你能更好的理解文中所涉及的原理和项目开发方案,我会配有大量的图文示例,让你理解起来不太困难,同时增加阅读的有趣性。

avatar

🔺 架构设计图

avatar

🔺 工具集合图

avatar

🔺 接口代码
avatar
🔺 增加 Hystrix 数据流代码

你将获得什么?

本专栏课程能帮你解决哪些问题呢?

  • 知识点散落在网络各处,需要花费大量时间成本去筛选梳理
  • 缺乏完整性参与一个微服务开发项目的机会,面对新机会时把握不住
  • 自学 Spring Cloud 稍有难度,学习进度缓慢,无法深刻掌握各组件在项目中实际的融合情况
  • 虽有个别案例代码,但没有配套的文档对应,理解起来难免会一知半解

通过本课程的学习,能收获什么呢?

  • 将 Spring Cloud 微服务架构开发体系串连起来,熟悉各个组件的用途,各组件间如何融合来共同完成一个功能特性
  • 系统性从 0 到 1 的创造一个产品,不管是去面试,还是在实际产品开发中应用,做到胸有成竹,迎接挑战
  • 掌握 Scrum 敏捷开发模式在实际研发中的应用情况
  • 接触到一些项目过程中的开发技巧,可以完整迁移到自己的工作中去

适读人群

  • Java 初中级后端开发者

  • 渴望往架构师方向发展的开发者

  • 想了解微服务架构体系的开发者

前置技能

  • 掌握 Java 基础知识,Maven 使用经验

  • 具备 Spring MVC 、 Spring Boot 、MyBatis 开发经验

作者介绍

购买须知

  • 本专栏为图文内容,共计 24 篇。
  • 付费用户可享受文章永久阅读权限。
  • 本专栏为虚拟产品,一经付费概不退款,敬请谅解。
  • 本专栏可在 GitChat 服务号、App 及网页端 gitbook.cn 上购买,一端购买,多端阅读。

订阅福利

  • 订购本专栏可获得专属海报(在 GitChat 服务号领取),分享专属海报每成功邀请一位好友购买,即可获得 25% 的返现奖励,多邀多得,上不封顶,立即提现。

  • 提现流程:在 GitChat 服务号中点击「我-我的邀请-提现」。

  • ①点击这里跳转至》第 4 篇《翻阅至文末获得入群口令。

  • ②购买本专栏后,服务号会自动弹出入群二维码和入群口令。如果你没有收到那就先关注微信服务号「GitChat」,或者加我们的小助手「GitChatty6」咨询。

课程内容

开篇导读

你好,我是码闻强。拥有 12 年软件开发管理经验,创业前服务于杭州知时信息科技有限公司,任高级系统架构师,带领团队研发出一套适合汽车流通行业的 SaaS 产品——指是金融及周边配套产品,为业内数十家机构提供线上支撑服务。

你是不是有这样的困惑?

  • 知道微服务开发热门,但一直是外行看热闹,不知道里面具体有哪些内容。
  • 了解过 Spring Cloud 微服务开发理论,苦于没实战经验。
  • 知道单个技术点的应用,但怎么将技术融合起来有些模糊。

为什么要学习微服务?

1. 求职时增强技术自信。 微服务是非常热门的话题,企业招聘中也越来越多的要求有微服务开发、架构能力,不掌握一些微服务技能找工作时也缺乏亮点,可以说微服务架构已经成为中高级后端开发人员、架构师的必备技能。

2. 提升技术实力,增加职业转型的可能性。 长期从事局部功能开发,会导致全局掌控能力缺失。软件系统是一个复杂工程,只有从更高的角度统观全局,考虑业务的方方面面以及未来可能的演进方向,才能深刻理解一个产品或项目的内在含义,而这个话语权往往掌握在更高职级的开发者、设计师、架构师手中,如果掌握了一套微服务架构、开发理念,增加了向更高职级晋升的可能性。

3. 解决工作中软件研发难题。 随着软件复杂度的提升,社会分工的越来越细,单体应用或者粗放式的软件服务,大量功能代码堆积在一起,显得特别臃肿繁杂,开发维护成本很高。这在日常运维、升级维护时非常不便,一个小功能的变更都有可能导致整个工程出现问题甚至宕机,如果是运行中的生产环境崩溃,由此所造成的经济损失或不好的社会影响,将是不可估量的。而引入微服务,可以更好的解决这一系列的问题。

4. 保持技术的前瞻性。 研发技术迭代日新月异,新概念新应用也是层出不穷,云原生架构、容器化运维、中台等等,都与微服务有着微妙的关系,只有保持技术的持续性,才能更好的学习新技术,否则会很不利于新技术的落地应用。

微服务有什么用?能解决实际业务场景中哪些问题?

将单体应用分拆成多个小体量的的高内聚低耦合的软件服务,可以采用不同的软件语言,不同的数据存储,每个小服务独立部署运行,服务间采用轻量级的通信机制,由不同的团队开发和维护,大大提高了软件的可维护性、扩展性。

采用微服务架构,可以帮助我们很好的解决实际业务场景中的问题:

  • 从组织层面讲,系统的分工更加明确,责任更加清晰。

  • 按照业务来划分服务,单个服务代码量小,业务单一,更易于维护。

  • 服务能力更专一,代码重复度降低,可复用性高,使服务调用更加简便。

  • 服务之间耦合性低,可随时加入服务,剔除过时服务。

  • 单个服务也可以弹性扩展,容错能力更强,

  • 更贴合云时代背景的软件开发运维环境,高效自动化构建部署。

  • 数据存储解耦,不必将所有数据集中在一个大库中,不易拆分扩容。

为什么选择 Spring Cloud?

Spring Cloud 的核心特性如下Spring Cloud 优点多多,从如下几点可以看出:

  • 源于 Spring,社区活跃、生态丰富、功能稳定,资料多,遇到问题很容易找到解决方案

  • 基于 Spring Boot,组件丰富,开箱即用,更便于业务落地。

  • 相比于其它框架,Spring Cloud 对微服务周边环境的支持力度更大。

  • 开发人员从业基数庞大,对于中小企业来讲,使用门槛较低。

  • 相当一部分企业产品实践,已经在使用 Spring Cloud 作为技术选型来落地应用,支撑实际业务。

再来看看 Spring Cloud 的生态组件有哪些,如何帮助我们快速建立微服务架构体系。

Spring Cloud 以全家桶的形式出现,提出了一整套的微服务解决方案,基本囊括了微服务体系中全部组件应用。每个组件又基于 Spring Boot 技术体系,提供开箱即用、约定优于配置的开发方案,可以使开发者们绕过繁琐的组件整合、环境配置等等,快速上手微服务。并且经过了 Spring 社区的整合,有大量的兼容性测试作基础,Spring Cloud 的稳定性是有保证的,我们也不用费尽心思的自己去拼微服务的各个积木块,而且也不一定比它更优秀。

国内开发者在纠结 Dubbo 与 Spring Cloud 的使用问题,Spring Cloud 不少功能组件的停更,环境搭建繁杂,近年阿里推出的 Spring Cloud Alibaba 组件迎头而上,上手更简单,学习曲线低,可更好与 Spring Cloud 生态融合在一起,强强联合,更加增加了 Spring Cloud 的在 Java 领域微服务开发的优势地位。

选择 Spring Cloud,不论是从开发人员储备、社区活跃度,还是厂商友好性、生态稳定性等角度考虑,都是做微服务架构选型一个良好选择。

专栏内容

本专栏就带你一起,将 Spring Cloud 技术融入到一个实际项目——“商场停车”中,一步一步将技术点串联起来,服务注册、服务发现、服务接口管理、配置中心、分布式事务、统一网关、服务限流降级等,一个点一个点慢慢啃,由点成线,由线成面,等专栏结束再回顾,Spring Cloud 微服务开发原来也没有想象中的那么难。主要内容分三个大模块:前期准备阶段,系统实现阶段以及高级应用部分。

  • 第一部分,准备篇,按照软件开发流程,进行早期的需求分析、系统设计、存储设计、架构设计工作,并搭建出架构雏形,介绍相关开发组件,为后期开发打好基础。

  • 第二部分,系统实现篇,结合实际业务功能一步一步完成微服务场景下所需的特性功能,包括服务的注册与发现、服务之间的调用、服务调用时快速失败/降级、配置中心、分布式缓存、面向端的服务适配、消息推送、网关路由、流量控制、统一鉴权等等。

  • 第三部分,开发进阶篇,在前期开发的基础上拔高,包括聚合 API 接口,对外提供统一入口;引入分布式事务,确保数据的完整性;加入分布式锁,防止限量产品出现超卖的情况;提供应用监控,实时观察各个系统应用实例的健康状态;引入链路追踪中间件,完整的串联系统的每次请求,监控系统调用的效率,为后期系统优化、服务可用性降级提供支撑依据。

你将获得什么?

本专栏课程能帮你解决哪些问题呢?

  • 知识点散落在网络各处,需要花费大量时间成本去筛选梳理
  • 缺乏完整性参与一个微服务开发项目的机会,面对新机会时把握不住
  • 自学 Spring Cloud 稍有难度,学习进度缓慢,无法深刻掌握各组件在项目中实际的融合情况
  • 虽有个别案例代码,但没有配套的文档对应,理解起来难免会一知半解

通过本课程的学习,能收获什么呢?

  • 将 Spring Cloud 微服务架构开发体系串连起来,熟悉各个组件的用途,各组件间如何融合来共同完成一个功能特性
  • 系统性从 0 到 1 的创造一个产品,不管是去面试,还是在实际产品开发中应用,做到胸有成竹,迎接挑战
  • 掌握 Scrum 敏捷开发模式在实际研发中的应用情况
  • 接触到一些项目过程中的开发技巧,可以完整迁移到自己的工作中去

适读人群

  • Java 初中级后端开发者

  • 渴望往架构师方向发展的开发者

  • 想了解微服务架构体系的开发者

前置技能

  • 掌握 Java 基础知识,Maven 使用经验

  • 具备 Spring MVC 、 Spring Boot 、MyBatis 开发经验

以真实“商场停车”业务切入——需求分析

不管是传统开发模式还是时下推崇的敏捷开发模式,都要从源头——业务需求开始着手,缺少了需求获取分析环节或本环节做的不够好,由此造成的错误会随着软件迭代的步伐,逐步被放大,近而造成巨大的损失。有时金钱成本尚可接受,但由此造成的时间成本、机会成本则是无法估量的。

为什么选取"商场停车业务"作为需求原型呢,有两个考量:一是场景熟知度比较高,业务理解上不存在很大障碍;二是能将我们所要表达的微服务特性融入进去,业务复杂度较低,便于上手开发,门槛太高反而不利于技术实践。

下面随着课程的步伐,一步一步深入进去吧。

停车收费需求

相信大家经常去商场购物,不管你有没有停过车,商场的停车场不会是个陌生的存在,本次实操就是以商场停车收费为业务原型开展。业务需求相对简单(实际业务要比案例中功能更复杂),主要是要能通过简单的业务功能掌握到我们关心的微服务技术栈即可。

主要干系人就是商场物业管理部,提出的原始需求如下:

  1. 用户手机号绑定商场的系统,在里面添加车辆,每天到商场附近时可以签到,领取积分,积分可以用于兑换限量的商场优惠券,也可以兑换洗车券,在停车场指定位置洗车。

  2. 用户可以通过系统开通 VIP 停车月卡,出入停车场时,无须额外付费。可以充值为月卡延期,充值时,为用户赠送一定数量的积分,可以做为正常积分使用。充值后,用户可以收到充值通知。

  3. 若用户未开通月卡,出场时,必须在系统里支付停车费用后,才能出场。费用多少按停车场约定的计费规则来计算。缴费后,用户能收到缴费通知。

  4. 用户进停车场前,能实时看到可用停车位数量。

  5. 在系统后台,可以看到用户的支付流水、充值流水,车位使用情况等

业务场景很清晰,也比较常见(实际场景中,本系统应当与其它系统打通,数据共享,便于数据分析、统计挖掘等,实际功能同样比这个更复杂些),本次就以如上的简单需求,结合微服务技术栈,来一次从 0 到 1 的完整性开发实践。

需求整理思路

Scrum 敏捷开发实践,在国内应用的时间也不算短,不少企业已经成功应用于产品迭代开发中,本次实战会融入敏捷思想,来指导这次的微服务开发实战。本文的需求分析工作多数情况下是由 PO(产品经理)完成。

按传统的瀑布研发模式来走,流程大致时需求分析,输出需求分析文档,而后进行概要设计、详细设计(数据库设计),并输出对应的文档,后续再进行编码工作。

敏捷中提倡可工作的文档高于详尽的文档,所以无须严格按照传统的模块输出一堆文档,能表述清楚、能与团队成员正确传达即可。

(图片来源于网站http://agilemanifesto.org/

基于原始需求,由 PO 输出用户故事列表形成需求池 Product Backlog,每个迭代中从中按优先级顺序,放入不同的迭代中形成 Sprint Backlog,同时分解故事为任务(故事的粒度要比任务粗,是一组功能的组合),由组员一起将任务工作量估算并认领。任务工作量汇总后,就形成了当前迭代的总工作量,基于燃尽图,随着时间的延长以及工作进度的推进,可以清晰的从燃尽图中看到任务的完成情况。可以借助于看板,将故事任务以不同色块的 3M 便利贴贴在白板上,画出待办列表、进行中列表、已完成列表,任务完成后由组员调整位置。

(工具绘制)

(图片来源于敏捷中文网)

为了提高工作效率,一般会借助对应的敏捷开发工具完成,比如 Teambition、JIRA、禅道等等,里面针对敏捷的各个环节都有很好的支持,相信能帮助大家节省不少成本。

需求分析

依据原始停车需求,整合分析后可归集为六个业务模块:

  1. 会员,会员信息(主要是姓名、手机号、生日),车辆信息(主要车牌信息),月卡信息(充值一定金额后,指定区间内免支付出入场,针对商场的工作人员或办公区的白领),签到信息
  2. 停车场资源,车位(车辆停靠车位后,系统自动打标,可用车位扣减,停车场入口可用车实时更新)、道闸(基础数据,真实场景中涉及到岗亭值班人员安排,设备维护等)
  3. 积分,会员到达商场后签到领取积分(如签到一次可免费停车 2 小时,超出部分,以计费规则为准)。积分可兑换商场优惠券、洗车券。
  4. 计费,计费规则,车辆进场(记录车辆入场时间,开始计费)、出场扣费(出场时识别车牌,依计费规则,支付停车费,分临时车与月卡车两种,临时车现场收费,月卡车做有效期判定是符合条件后再出场)
  5. 财务,车辆出入的支付流水,会员月卡充值费用,收入统计
  6. 消息,车辆出场支付消息,会员充值消息
  7. 兑换洗车券后,停车场内洗车消费

用户故事、任务整理

以上述需求归集情况,罗列几个代表性的用户故事,取代传统的需求文档。约定:商场停车系统以下简称为系统

  1. 作为一个普通用户,我希望可以使用手机号绑定手机号,并录入车辆信息,以便于可以离场时支付费用,接受支付结果通知。

  2. 作为一个系统用户,我希望可以开通停车场月卡,同时可额外获得积分,以便于在离场时无须额外支付,快速通过。

  3. 作为一个系统用户,我希望可以通过系统日常签到,得到积分,以便于可以兑换商场优惠券或停车场洗车券。

  4. 作为一个普遍用户/系统,我希望可以在进入停车场前,识别可用车位数,以便我做出相应的决策。

  5. 作为一个系统月卡用户,为月卡充值时收到消费通知,同时得到额外的积分。

  6. 作为一个系统用户,离场时需要按计费规则支付对应的停车费,并接收支付通知。

  7. 作为一个系统管理用户,我希望可以看到所有场内车位的使用情况、用户的支付流水、会员月卡的充值流水。

基于以上故事,将故事拆解成较细的任务,拿第一个 User Story 举例。

1.1 商场系统中绑定手机号,通过手机验收码,确定是本人手机。

1.2 完善个人信息,可以保存个人姓名、生日等信息

1.3 录入车辆车牌信息,后期出场车牌识别后,自动计算车辆停车费

罗列出故事的具体任务后,开发人员针对任务情况,进行工作量评估,敏捷估算工作量常用用部分斐波那契数列(小时数):0 ,1 ,2 ,3 ,5 ,8, 13, 21 等,建议不超过 8 时,超过 8 意味着需要进一步分拆,争取在一天内可以完成一个任务。估算需要选取一个基准复杂度,比如一次 DB 交互为 1,在此基础上,分析每个故事的复杂度,结合前面的工作量估算方法就可以给某个任务确定预估工时数。

有朋友会比较担心,万一预估的时间少了完不成怎么办,其实这只是初步预估,在实际执行时,以实际执行为准,没有一个敏捷团队的迭代燃尽图会像理想状态一般燃烧。

实操过程中不少小伙伴存在一个误区:关注花费过的时间,而不是关注剩余工作时间,燃尽图中显示是剩余工时数量。

关键业务流程梳理

基于需求情况,找出核心的业务流程,指导后期的核心业务开发。

小结:

本篇带你结合 Scrum 敏捷思想进行产品需求的分析,由 PO 人员整理用户故事、核心业务流程,由开发人员认领故事,分解故事为具体任务,并预估工作量,录入敏捷辅助工具中,为期开发人员介入作好准备工作。

留 2 个节后实践题目

  1. 初中级的开发者普遍不重视需求,更追求技能的提升,如果要走的快走的远,一定要坚持把技术、业务一起抓。尝试将所有 User Story 罗列出来吧,并分解为 Task 录入到一个特定的敏捷开发辅助工具中去,并结合敏捷开发全流程,跟随项目的实战全程体验一番。

  2. 敏捷真的不需要留存文档吗?

具象业务需求再抽象分解——系统设计

经过前篇需求梳理,商场停车收费业务需求情况已经十分明了,本节就依据前文的输出做为输入,开始系统设计工作,包括功能模块设计、存储设计、架构设计等,为后面的编码提供良好的基础保障。

有同学可能会有疑问,都使用敏捷了,怎么还要设计,直接上手编码不就行了?敏捷提倡响应变化,减少文档,很多朋友有误解,以为敏捷就是不需要设计,不需要文档,就是要快,凡是阻碍实施交付的都要省掉。其实,敏捷并不是消灭文档,消灭设计,关键性的文档、图片、设计还是要留存的,比如存储设计、关键业务流程设计等,但并不局限于是文档形式,可以是白板上或 A4 纸上的草图,也可以是便利贴,也可以是正式的文档等,能表达意思即可,方便后期追溯。

数据实体联系

基于以上业务情况,按领域划分为七个小模块,每个模块中划分出相应实体、事件,通过软件简略画出关键数据实体-联系图(未包含所有实体),如下图所示:

  1. 会员,车辆,月卡(绑定手机号,录入车辆,开月卡)
  2. 车位,闸机(车辆停靠、车辆离开)
  3. 积分(签到、兑换)
  4. 计费规则(入场、出场)
  5. 交易流水(支付、充值)
  6. 消息(推送)
  7. 洗车

业务模块设计

据第一篇需求分析的情况,我们已经识别出关键流程、主要业务模块以及模块中主要的业务实体、实体相关的事件。本案例完全可以采用单实体的模式开发,但为了模拟微服务开发的场景,所以这里按照微服务的设计方式来进行。

根据关键业务实体联系与事件,将业务模块整合为七个子服务。

  • 会员服务,包括会员信息、车辆信息、会员月卡
  • 基础资源服务,包括车位、闸机,车辆停靠记录
  • 计费服务,车辆出入场记录,计费规则
  • 积分服务,积分兑换,会员积分,会员签到得积分
  • 财务服务,支付流水,充值流水,财务统计
  • 消息服务,记录通知内容
  • 洗车服务,积分兑换的洗车券去场内洗车

服务的拆分粒度究竟多细,业界并没有统一的标准,必须依据公司团队情况、人员能力水平以及产品的使用情况来划分,不可过细,过粗也失去了微服务的意义。 每个微服务可交由二到三个开发人员维护,避免维护过多,分散精力,同时又可保证快速的响应维护升级。

存储设计

微服务架构风格的一个好处,是持久性的封装。我们可以根据每个服务的需要,去选择不同的持久化技术。根据每种数据类型的特点而去选择数据存储的方法,也就是混合持久化,结构化存储与非结构化存储混合使用。不同服务间使用不同的存储模型,单一服务中也可以使用混合存储。既然要采用微服务化结构,从独立开发、运行、部署和运维都是单独的小应用。每个小应用内部业务逻辑处理,到数据库访问,以及数据库都是独立的。

依据本案例的业务场景,我们拆分为七个子存储库,分别为:

  • park_member——会员服务库
  • park_resource——停车场资源服务库
  • park_charging——计费服务库
  • park_card——积分服务库
  • park_finance——财务服务库
  • park_message——消息服务库
  • park-carwash——洗车服务库

实际中有些实施微服务的团队,将服务拆分,但存储库依旧仍是一份,现实中应该有为数不少的存在。不能说不对,只能说不符合微服务的建议。

结构化存储采用社区版 MySQL 5.7+版本,非结构存储主要涉及到缓存这块,采用 Redis 4.0 +版本。结构化存储中建议设计一些通用字段,主要用于跟踪数据记录,库表结构通用字段如下:

  `create_by` varchar(32) DEFAULT NULL COMMENT '创建人',  `create_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',  `update_by` varchar(32) DEFAULT NULL COMMENT '更新人',  `update_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新日期',  `remark` varchar(500) DEFAULT NULL COMMENT '备注',  `version` int(4) DEFAULT '0' COMMENT '版本',  `state` int(4) DEFAULT '1' COMMENT '状态'

每条数据记录的创建人、创建时间,后续的更改人、更改时间,非业务层面的备注、版本、状态,有利于数据维护人员识别,建议每个表中都加上。

建库脚本如下

CREATE DATABASE `park_member` CHARACTER SET utf8 COLLATE utf8_general_ci;CREATE DATABASE `park_resource` CHARACTER SET utf8 COLLATE utf8_general_ci;CREATE DATABASE `park_charging` CHARACTER SET utf8 COLLATE utf8_general_ci;CREATE DATABASE `park_card` CHARACTER SET utf8 COLLATE utf8_general_ci;CREATE DATABASE `park_finance` CHARACTER SET utf8 COLLATE utf8_general_ci;CREATE DATABASE `park_message` CHARACTER SET utf8 COLLATE utf8_general_ci;CREATE DATABASE `park_carwash` CHARACTER SET utf8 COLLATE utf8_general_ci;

park_member 库初始化表结构

-- ------------------------------ Table structure for member-- ----------------------------DROP TABLE IF EXISTS `member`;CREATE TABLE `member` (  `id` varchar(32) NOT NULL DEFAULT '',  `phone` varchar(11) DEFAULT NULL COMMENT '手机号',  `birth` varchar(10) DEFAULT NULL COMMENT '生日',  `full_name` varchar(20) DEFAULT NULL COMMENT '姓名',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员信息';-- ------------------------------ Table structure for month_card-- ----------------------------DROP TABLE IF EXISTS `month_card`;CREATE TABLE `month_card` (  `id` varchar(32) NOT NULL DEFAULT '',  `card_no` varchar(20) DEFAULT NULL COMMENT '会员卡号',  `start` varchar(16) DEFAULT NULL COMMENT '有效期起始',  `ends` varchar(16) DEFAULT NULL COMMENT '有效期截止',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员月卡信息';-- ------------------------------ Table structure for vehicle-- ----------------------------DROP TABLE IF EXISTS `vehicle`;CREATE TABLE `vehicle` (  `id` varchar(32) NOT NULL DEFAULT '',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  `plate_no` varchar(10) DEFAULT NULL COMMENT '车牌号',  `vehicle_inf` varchar(50) DEFAULT NULL COMMENT '车辆型号',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员车辆';

park_resource 库初始化表结构

-- ------------------------------ Table structure for brake-- ----------------------------DROP TABLE IF EXISTS `brake`;CREATE TABLE `brake` (  `id` varchar(32) NOT NULL DEFAULT '',  `code` varchar(20) DEFAULT NULL COMMENT '编号',  `desc` varchar(50) DEFAULT NULL COMMENT '描述',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='停车场闸机';-- ------------------------------ Table structure for stall-- ----------------------------DROP TABLE IF EXISTS `stall`;CREATE TABLE `stall` (  `id` varchar(32) NOT NULL DEFAULT '',  `code` varchar(10) DEFAULT NULL COMMENT '编号',  `is_parked` int(2) DEFAULT NULL COMMENT '是否被占用',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车位表';-- ------------------------------ Table structure for stall_parked-- ----------------------------DROP TABLE IF EXISTS `stall_parked`;CREATE TABLE `stall_parked` (  `id` varchar(32) NOT NULL DEFAULT '',  `stall_id` varchar(32) DEFAULT NULL COMMENT '车位编号',  `plate_no` varchar(30) DEFAULT NULL COMMENT '车牌',  `mtype` int(2) DEFAULT NULL COMMENT '0 入场,1 出场',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车位停靠记录';

park_charging 库初始化表结构

-- ------------------------------ Table structure for charging_rule-- ----------------------------DROP TABLE IF EXISTS `charging_rule`;CREATE TABLE `charging_rule` (  `id` varchar(32) NOT NULL DEFAULT '',  `start` int(4) DEFAULT NULL COMMENT '停车时间起始',  `end` int(4) DEFAULT NULL COMMENT '停车时间结束',  `fee` float DEFAULT NULL COMMENT '收费',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='计费规则';-- ------------------------------ Table structure for entrance-- ----------------------------DROP TABLE IF EXISTS `entrance`;CREATE TABLE `entrance` (  `id` varchar(32) NOT NULL DEFAULT '',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  `plate_no` varchar(10) DEFAULT NULL COMMENT '车牌',  `brake_id` varchar(32) DEFAULT NULL COMMENT '闸机号',  PRIMARY KEY (`id`) USING BTREE,  KEY `no_idx` (`plate_no`),  KEY `member_idx` (`member_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车辆入场';-- ------------------------------ Table structure for vexists-- ----------------------------DROP TABLE IF EXISTS `vexists`;CREATE TABLE `vexists` (  `id` varchar(32) NOT NULL DEFAULT '',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  `brake_id` varchar(32) DEFAULT NULL COMMENT '闸机号',  `plate_no` varchar(32) DEFAULT NULL COMMENT '车牌号',  PRIMARY KEY (`id`) USING BTREE,  KEY `no_idx` (`plate_no`),  KEY `member_idx` (`member_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车辆驶出';

park_card 库初始化表结构

-- ------------------------------ Table structure for exchange-- ----------------------------DROP TABLE IF EXISTS `exchange`;CREATE TABLE `exchange` (  `id` varchar(32) NOT NULL DEFAULT '',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  `card_qty` int(11) DEFAULT NULL COMMENT '积分数量',  `ctype` int(4) DEFAULT NULL COMMENT '0 优惠券,1 洗车券',  `code` varchar(30) DEFAULT NULL COMMENT '券编码',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='积分兑换';-- ------------------------------ Table structure for member_card-- ----------------------------DROP TABLE IF EXISTS `member_card`;CREATE TABLE `member_card` (  `id` varchar(32) NOT NULL DEFAULT '',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  `cur_qty` varchar(20) DEFAULT NULL COMMENT '当前可用积分',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员积分';-- ------------------------------ Table structure for member_sign-- ----------------------------DROP TABLE IF EXISTS `member_sign`;CREATE TABLE `member_sign` (  `id` varchar(32) NOT NULL DEFAULT '',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  `cnt` int(4) DEFAULT NULL COMMENT '积分数量',  `ctype` int(4) DEFAULT NULL COMMENT '0 签到,1 商场消费',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员签到';

park_finance 库初始化表结构

-- ------------------------------ Table structure for billing-- ----------------------------DROP TABLE IF EXISTS `billing`;CREATE TABLE `billing` (  `id` varchar(32) NOT NULL DEFAULT '',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  `fee` float DEFAULT '0' COMMENT '支付金额',  `plate_no` varchar(10) DEFAULT NULL COMMENT '车牌号',  `duration` float DEFAULT '0' COMMENT '停车时间',  PRIMARY KEY (`id`) USING BTREE,  KEY `no_idx` (`plate_no`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='车辆驶出支付流水';-- ------------------------------ Table structure for month_card_recharge-- ----------------------------DROP TABLE IF EXISTS `month_card_recharge`;CREATE TABLE `month_card_recharge` (  `id` varchar(32) NOT NULL DEFAULT '',  `card_no` varchar(20) DEFAULT NULL COMMENT '月卡号',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  `amount` float DEFAULT NULL COMMENT '充值金额',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='会员月卡充值';

park_message 库初始化表结构

-- ------------------------------ Table structure for message-- ----------------------------DROP TABLE IF EXISTS `message`;CREATE TABLE `message` (  `id` varchar(32) NOT NULL DEFAULT '',  `mtype` char(10) DEFAULT NULL COMMENT '消息类型,PAY 支付消息,BIND 绑定信息',  `mcontent` varchar(500) DEFAULT NULL COMMENT '消息内容',  `member_id` varchar(32) DEFAULT NULL COMMENT '会员',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='推送消息';

park_carwash 库表结构

-- ------------------------------ Table structure for car_wash-- ----------------------------DROP TABLE IF EXISTS `car_wash`;CREATE TABLE `car_wash` (  `id` varchar(32) NOT NULL,  `member_id` varchar(32) DEFAULT NULL COMMENT '会员编号',  `plate_no` varchar(10) DEFAULT NULL COMMENT '车牌号',  `ticket_code` varchar(20) DEFAULT NULL COMMENT '洗车券编码',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8;

完整的表结构脚本地址,点击下方链接:

https://github.com/backkoms/spring-cloud-alibaba-ParkingLot/tree/master/src/script

初始化数据

有了初步数据库的模型,需要初始化一部分数据进去,比如计费规则、闸机信息,车位信息。

闸机数据

INSERT INTO `brake` VALUES ('4edb0820241041e5a0f08d01992de4c0', 'ct1', '入场口', 'admin', '2019-12-27 11:37:22', NULL, '2019-12-27 11:37:22', NULL, 0, 1);INSERT INTO `brake` VALUES ('989170c529a348b3b93bf2a4653e8ea9', 'ct2', '入场口', 'admin', '2019-12-27 11:37:45', NULL, '2019-12-27 11:37:45', NULL, 0, 1);INSERT INTO `brake` VALUES ('e489029055654bccb3cd601f0be71c41', 'ct3', '出场口', 'admin', '2019-12-27 11:37:36', NULL, '2019-12-27 11:37:36', NULL, 0, 1);INSERT INTO `brake` VALUES ('f726873ed17441ea8dfbf78381bcde78', 'ct4', '出场口', 'admin', '2019-12-27 11:37:41', NULL, '2019-12-27 11:37:41', NULL, 0, 1);

车位数据

INSERT INTO `stall` VALUES ('004ac347b94e42bb8f0f6febd3265e35', 'P336', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);INSERT INTO `stall` VALUES ('008773e089664ce49607c86b89dd8c0f', 'P250', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);INSERT INTO `stall` VALUES ('0110ef02554f46ce91a3eeec6ecf2f95', 'P224', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);INSERT INTO `stall` VALUES ('014f1f2b972e4e0092d749a7437f824d', 'P577', 0, 'admin', '2019-12-27 11:42:04', NULL, '2019-12-27 11:42:04', NULL, 0, 1);INSERT INTO `stall` VALUES ('019f4aa0c22849e1a5758aaa33b855df', 'P229', 0, 'admin', '2019-12-27 11:42:03', NULL, '2019-12-27 11:42:03', NULL, 0, 1);

计费规则

INSERT INTO `charging_rule` VALUES ('41ed927623cf4a0bb5354b10100da992', 0, 30, 0, 'admin', '2019-12-27 11:26:08', NULL, '2019-12-27 11:26:08', '30 分钟内免费', 0, 1);INSERT INTO `charging_rule` VALUES ('41ed927623cf4a0bb5354b10100da993', 31, 120, 5, 'admin', '2019-12-27 11:26:12', NULL, '2019-12-27 11:26:12', '2 小时内,5 元', 0, 1);INSERT INTO `charging_rule` VALUES ('4edb0820241041e5a0f08d01992de4c0', 121, 720, 10, 'admin', '2019-12-27 11:34:06', NULL, '2019-12-27 11:34:06', '2 小时以上 12 小时以内,10 元', 0, 1);INSERT INTO `charging_rule` VALUES ('7616fb412e824dcda41ed9367feadfda', 721, 1440, 20, 'admin', '2019-12-27 13:35:37', NULL, '2019-12-27 13:35:37', '12 时至 24 时,20 元', 0, 1);

非结构化存储

主要使用 Redis 中间件来存储可用车位的实时性信息,计费规则信息等热数据。

架构设计

没有最优的架构,只有最合适的架构,一切系统设计原则都要以解决业务问题为最终目标,并随着业务的发展,不断进行迭代演进。经过上面业务模块、存储模型的划分,基本的代码架构已经清晰可见。综合业务模块、微服务架构特性,输出功能架构设计图。

基于总体功能架构图,使用特定的功能组件即可实现相应的功能。前期也提到,Spring Cloud 全家桶中囊括了很多组件,开箱即用,这对快速上手微服务开发提供了极大的便利。同时,再融入时常开发实践一些常用的高效工具来提升编码效率如 Lombok,MBG 等。

留个思考题

组件库有提成 Lombok,对简化代码开发很有帮助。你有什么好用的组件库,能够在项目开发中,高效发挥作用呢?

第一个 Spring Boot 子服务——会员服务
如何维护接口文档供外部调用——在线接口文档管理
认识 Spring Cloud 与 Spring Cloud Alibaba 项目
服务多不易管理如何破——服务注册与发现
如何调用本业务模块外的服务——服务调用
服务响应慢或服务不可用怎么办——快速失败与服务降级
热更新一样更新服务的参数配置——分布式配置中心
如何高效读取计费规则等热数据——分布式缓存
多实例下的定时任务如何避免重复执行——分布式定时任务
同一套服务如何应对不同终端的需求——服务适配
采用消息驱动方式处理扣费通知——集成消息中间件
Spring Cloud 与 Dubbo 冲突吗——强强联合
破解服务中共性问题的繁琐处理方式——接入 API 网关
服务压力大系统响应慢如何破——网关流量控制
集成网关后怎么做权限隔离——统一鉴权
如此多服务模块,接口如何管理——聚合 API
数据分库后如何确保数据完整性——分布式事务
优惠券如何避免超兑——引入分布式锁
如何查看各服务的健康状况——系统应用监控
如何确定一次完整的请求过程——服务链路跟踪
结语

阅读全文: http://gitbook.cn/gitchat/column/5eb0d962d57c4106ecc28597

  • 0
    点赞
  • 2
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论

打赏作者

蔚1

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值