关注上方蓝色“Java高性能架构”,设为星标⭐
回复“资料”获取整理好的面试资料
风哥和朋友原创的视频教程有兴趣的可以看看:
业务服务是业务层对外的统一门面,它由三方面组成:业务接口、入参、出参。另外,搜索公众号Linux就该这样学后台回复“电子书”,获取一份惊喜礼包。
a) 业务接口
一个业务接口代表一个领域的业务服务,比如订单域的业务服务就由接口OrderService表示,会员域的业务服务就由接口MemberService表示。接口可以按照执行性质分为读接口和写接口,比如OrderReadService和OrderWriteService。读写分离的好处是可以对集群进行读写分组,从而管理流量,当然,单机系统读写分离意义不是太大。领域内的操作则以业务接口中的方法的形式体现,比如订单域有下单createOrder,取消订单cancelOrder等等操作。对于这些操作,尽量设计出有业务含义的方法,而不是增删改查,当然,对于一些简单的业务,也只能增删改查。
b)入参
接下来,是入参的设计。入参对于读方法,比较简单,不做讨论。对于写方法,我们将入参设计成有层次的数据模型。首先需要设计出公共的数据模型,比如订单数据模型,商家数据模型,商品数据模型等,然后将这些数据模型和一些特定业务下的个性数据结合,组成Request对象,这个request对象按照不同业务操作不同而不同,对应的返回结果就是response,它也是随着不同业务返回的参数不同。
举个例子,拿下餐饮订单来说,首先,我们应该识别出这些业务流程中一些比较基础的数据模型,比如餐饮领域的菜品、桌位等,这些模型之所以说是基础模型,是因为,不管下什么餐饮订单,菜品和桌位肯定是逃不了的,它们是可以被复用的!因此,我们分别为这些基础模型设计相对于的DO(Domian Object):DishDO(菜品)、BoardDO(桌位)等等,接下来,我们为下餐饮订单设计一个请求对象DishOrderCreateRequest其中DishOrderCreateRequest内部包含了DishDO和BoardDO,另外会包含一些特定的属性,比如人数啊,折扣啊等等,这样一来就能做到通用和灵活兼顾,DishOrderCreateRequest代表的个性化的灵活的业务入参,而DishDO和BoardDO等则代表了不易变化的基础模型。
c) 出参
最后,是出参的设计。对于写方法,一般出参比较简单。对于读方法,出参往往是一个结构与层次比较复杂的组合对象。比如查询一个订单,这个订单有订单基本信息,还有商品信息,收货人地址信息等。在设计出参的时候,结构上要设计成组合对象,但是真正查询的时候,通过查询选择器,去查询不同的组合对象。比如查询选择器设置商品查询为true,地址查询为false,那么这次查询出的订单就只包含商品,而不包含地址。
业务流程中可以大致分为三种动作节点,1、组装参数节点 2、规则判断节点 3、执行动作节点,其中每个动作节点都是一些业务代码的片段。举个例子,下餐饮订单,我们第一步就是将上层传入的参数组装出一个基础的DishOrderDO(组装参数节点),然后按照特定的规则去填充这个DishOrderDO(规则判断节点),然后就是调用DAO去创建DishOrderDO(执行动作节点)。
业务流程是最容易变化的地方,要想维护好业务流程并不容易,总的思想是将大的业务流程拆分成小的业务流程,抽出每个业务流程中共有的代码片段,变成可维护的业务组件。
a) 基础组件
业务组件其实是将一些内聚的可复用的代码片段进行封装。和业务流程中的三种业务节点相对应,业务组件也分为三种:组装参数组件 、规则判断组件 、动作执行业务组件。业务组件的抽象往往是对业务有了深刻理解之后才进行的,盲目地进行业务组件的抽象,往往到头来白忙活。
b) 能力
对业务组件进行进一步抽象,可以得到能力。业务能力是具有一定复用性的组件的组合,比如发短信能力=组装短信参数组件+发短信组件。对于发短信能力,可以被不同的业务流程复用,比如订单下单成功发短信,支付成功发短信,逻辑都是相似的,只有内容不同。能力是一种粒度比较大的组件,粒度越大,往往复用性就越小,对能力的抽取,也是基于对特定业务深刻的理解,没有一劳永逸的银弹。
c)更高纬度的抽象
经过本人的实践,对于互联网这样的需求变化极快的场景,更高纬度的组件抽象往往性价比很低,不建议大家去做。
接口定义是按照不同的技术框架,同时结合业务需要,设计出合理的接口,对于业务组件来说,它们只会感知技术接口,而不会去感知技术实现,我们也不应该将具体的技术细节向上暴露,这也就是所谓的面向接口编程。技术接口往往是业务与技术之间的桥梁,接口本身是含有业务含义的,最常见的就是DAO接口,我们设计DAO接口的时候,不会设计成insert、update、query这样业务无关的接口,而是设计成insertUser,updateUserById等等和业务相关的接口,同样的道理,设计缓存接口的时候,也不能设计成put、get这样的接口,而应该设计成cacheUser,deprecateUser这样的接口。
数据存储:数据存储包括关系型数据库、非关系型数据库以及文件存储系统。关系型数据库,比如MySQL,适合存放绝大部分业务数据。非关系型数据库,比如hbase,可以存放历史日志,也可以对历史的MySQL数据进行归档。文件存储系统,一般都是基于Linux文件系统,比如图片、html文件等等,也有基于HDFS的,用于大数据分析。
缓存:缓存按响应时间分,可以分为纳秒级缓存,毫秒级缓存和百毫秒级缓存。纳秒级缓存就是一般的基于本地内存的缓存,比如encache,毫秒级缓存一般是集中式的内存缓存,比如memcache,由于访问时远程调用,因此响应时间会延长到几毫秒,百毫秒级缓存一般是集中式可持久化的缓存,比如redis,由于存在远程访问以及缓存击穿导致的读取持久化记录,它的响应时间会更长些,到几十甚至上百毫秒。单机系统一般用本地内存缓存就够了,当缓存被击穿的时候,直接访问数据库。
消息和调度任务:消息和调度任务本质都是一种异步化的手段,区别在于消息无法控制异步的时间,而调度任务可以。一般,消息发送出去后,监听消息的系统会立即收到消息,从而立即触发业务逻辑的执行,而调度任务则会按照调度规则,一次或者多次的执行业务逻辑。单机系统中消息和调度任务用到的比较少,在做日志监控的时候可能会用到消息,在进行数据报表统计的时候可能会用到调度任务。
事务:事务本质都是基于数据库去实现的,单机系统的事务就是依赖数据库的事务,我们可以使用spring-tx的事务模板进行事务操作,在业务逻辑开发中,一定要把握事务的大小,建议把业务比较紧密的一堆数据库操作放在一个事务里,不要随意的为每个方法都开启事务。
锁:单机系统中主要用到两种锁:乐观锁和悲观锁。乐观锁依靠在数据库的业务表加版本字段来实现,每次更新都会去判断版本是否变化,如果变化则需要重试,这种锁的粒度比较小。悲观锁是基于JDK的Lock接口的,对一个业务流程进行加锁和释放锁的操作,锁的粒度比较粗。
end
风哥与朋友波哥一起联合打造了《Java架构师成长之路》的视频教程。完全对标外面2万左右的培训课程。
除了基本的视频教程之外,还提供了超详细的课堂笔记,以及源码等资料包..
课程阶段:
Java核心 提升阅读源码的内功心法 深入讲解企业开发必备技术栈,夯实基础,为跳槽加薪增加筹码 分布式架构设计方法论。为学习分布式微服务做铺垫 学习NetFilx公司产品,如Eureka、Hystrix、Zuul、Feign、Ribbon等,以及学习Spring Cloud Alibabba体系 微服务架构下的性能优化 中间件源码剖析 元原生以及虚拟化技术 从0开始,项目实战 SpringCloud Alibaba电商项目 点击下方超链接查看详情(或者点击文末阅读原文):
(点击查看) 2023年,最新Java架构师成长之路 视频教程!
以下是课程大纲,大家可以双击打开原图查看