Roses基于Spring Boot 2和Spring Cloud Finchley.SR2,致力做整套分布式和服务化解决方案,Roses拥有高效率的开发体验,提供可靠消息最终一致性分布式事务解决方案,提供基于调用链的服务治理,提供可靠的服务异常定位方案(Log + Trace)等等,一个分布式框架不仅需要构建高效稳定的底层开发框架,更需要解决分布式带来的种种挑战。
Roses企业级微服务视频教程
Roses企业级微服务视频教程,详情点击https://www.stylefeng.cn/rosesVideo
Guns VIP计划
Guns VIP计划,详情点击https://www.stylefeng.cn/gunsVIP
最新更新说明
Roses目前的版本为Roses 2.0,原有项目代码拆分为多个子项目存放在Roses项目组里,框架和业务分开维护,更快捷的开发新业务。
Roses项目组地址:https://gitee.com/stylefeng-Roses
Roses会持续更新下去,您可以star & watch本项目,以了解Roses的最新动态,感谢您的支持!
Roses子项目介绍
模块名称说明地址roses-kernelRoses框架核心支撑https://gitee.com/stylefeng-Roses/roses-kernelroses-biz-support基础业务服务支撑https://gitee.com/stylefeng-Roses/roses-biz-supportroses-cloud-support微服务支撑组件https://gitee.com/stylefeng-Roses/roses-cloud-supportroses-config-repo配置仓库https://gitee.com/stylefeng-Roses/roses-config-reporoses-system系统管理服务https://gitee.com/stylefeng-Roses/roses-systemroses-gateway微服务网关https://gitee.com/stylefeng-Roses/roses-gateway
为什么Roses 2.0拆分项目
相信大家所在的公司都有自己的gitlab仓库,或者托管在码云平台,仓库中有很多项目,很多业务模块,每个业务模块都配置了spring mvc,mybaits,mybaits plus之类的,每个 项目都有一些共性的东西,当您开发一个微服务的时候,您是直接从旧项目复制一套东西,在新项目改一改名字,包名,类名更方便,还是直接新建一个项目,引入一个maven的pom 更方便。还有就是框架上的东西升级的时候,同样的类,同样的方法,您一定不喜欢修改两次或者更多次。
可能有的人会反驳这个观点,直接建一个大项目,里边建立多个maven模块来构建,新建微服务的时候我直接在下边加个模块不可以吗?
也可以,但是不推荐,到了项目后期,模块越来越多,可能光导入项目机器都很卡,还有开发A模块的人,公司规定不准看B模块的业务,这种情况无法解决。随着公司的发展,公司一定不是只有一个项目。
所以拆分项目,更加适合公司实际业务开发的情况,共有的东西提出来,以maven模块方式依赖进业务系统。升级框架的时候,通知业务负责人升级pom 版本即可。业务人员也可以更加专注于开发业务,导入项目开发时,也只需要导入开发人员开发的模块即可,非常方便。
使用手册
如果是刚接触微服务,可能有些吃力,不要气馁,相信自己,花点时间一定可以跑起来。
由于拆分了项目,使用方式略有不同,不了解maven的同学,需要先学习下maven,另外项目还用了lombok,需要安装lombok插件。
请先导入roses-kernel,根pom下执行mvn clean install -Dmaven.test.skip=true或者deploy到自己公司的私服上。
再导入roses-biz-support,根pom下执行mvn clean install -Dmaven.test.skip=true或者deploy到自己公司的私服上。
之后需要先启动配置中心,再启动注册中心,仓库地址地址在https://gitee.com/stylefeng-Roses/roses-cloud-support上面,具体启动方法见仓库里readme
然后启动roses-system,仓库地址地址在https://gitee.com/stylefeng-Roses/roses-system,具体启动方法见仓库里readme
再启动roses-gateway网关,仓库地址在https://gitee.com/stylefeng-Roses/roses-gateway,具体启动方法见仓库里readme
微服务的优缺点
微服务不一定适用于所有的系统构建,需要根据各自公司业务情况来评估。以下列举一些微服务的优缺点,仅供参考。
1. 微服务的优势
- 单体服务就像在一个小超市中,一个人负责收银,打扫,摆货,咨询等各种事情,服务化就像是一大超市,采取分工的方式更好的服务顾客,更加高效。
- 多业务并行开发更加容易,单体应用按业务拆分后,一个微服务只需要关注某个方面的业务,例如订单,用户,积分,账户服务等,同时并行开发,代码提交互不影响,每个人只需要关注自己的领域迭代。
- 上线发布不会因为某个微服务升级,影响到所有服务,每次某个微服务升级,只需要重新部署一个服务即可。
- 技术栈不受限制,在微服务架构中,服务之间调用可用restful api方式进行交互,所以技术选型可以根据不同部门的需要设计不同的服务。
- 通过对服务的划分,当系统中某些业务出现瓶颈时,可根据需要进行升级,或者直接增加某些服务的机器。
2. 微服务面临的挑战
- 服务多级调用带来的调试,跟踪困难。访问一个业务功能,要经过网关,之后调用A服务,B服务,C服务,通常会先去寻找A服务的问题,之后去排查B服务,以此类推,排查所有服务后,也可能问题不在A,B,C三个服务上,也许只是A访问B时调用超时了,所以这就导致了服务出现问题,定位非常困难。
- 分布式事务问题。A服务调用B服务,之后A又调用C服务,若C服务执行时出现异常,那么如何回滚B服务的执行过程。
- 运维成本。更多的服务意味着更多的运维投入,单体架构中,只需部署一个项目即可,微服务中,少则部署数个多则部署几十个服务,并且某些服务可能为集群部署,机器的消耗和运维要比单体服务提升很多。
- 开发成本,对开发人员的技术要求和开发一个业务的工作量有所不同。单体服务中,没有幂等性和分布式事务等问题,微服务开发中,不仅需要编写业务,还要考虑到可能会影响业务正常运行的其他因素。
一些建议
- 如果您觉得分布式项目不适合您,不妨考虑一下作者的另一款单体项目框架Guns,Guns项目代码简洁,注释丰富, 上手容易,是一个后台管理系统的脚手架,会让您降低不少开发成本!
- 刚接触分布式框架的同学,请利用好手头的资源,运行前仔细阅读使用手册,按照步骤一步步来,一定可以成功运行!
Roses特点
1. 快速开发微服务的秘籍 roses-kernel
在roses-kernel模块的com.stylefeng.roses.core.config包下整合了大量开发常用到的配置,其中包含默认异常拦截,登陆用户的上下文获取,默认缓存配置,默认fastjson的配置,默认mybatis-plus的配置,默认的swagger的配置,默认的web配置等等等等,使得在新业务开发中,只要pom引入roses-kernel这个模块,即可很方便的注入这些特性,直接上手开编写业务,大大减少了新业务,新模块的配置,调试,各种框架集成拼接的时间,因为这些在Roses中已经为您提供好了,利用Spring Boot的自动配置机制,同样的,这些配置在项目启动的时候会默认加载,因为在roses-kernel模块下的META-INF/spring.factories中配有这些类,当然,如果您不需要某些特性(自动配置类)您可以在@SpringBootApplication注解上增加exclude参数来排除这些自动配置。
有了roses-kernel模块,开发别的模块时,您可以把百分之90的精力花在编写业务上,百分之10的精力花在搭建项目和配置项目上。
2. 网关实现统一鉴权,资源权限过滤
Roses中,所有业务请求经过网关,网关做统一的鉴权,权限过滤,数据签名校验,这样做的好处就是业务系统中不用集成鉴权模块,只需要关心各自服务的业务编写。鉴权方面Roses依旧使用jwt,如果想对某个用户作某些资源访问的限制,需要开启PathMatchFilter,该过滤器会对用户当前访问的资源进行权限校验,资源的收集方式也做了大大的简化,Roses中通过资源搜集器roses-scanner收集资源(详见特点6),所有的资源只需要用@ApiResource注解标识,项目运行后会把所有资源发送到roses-auth服务中,对比以往的每新增一个资源都需要在后台管理系统中新增一条记录,省了不少工作量。
3. 网关生成唯一请求号(RequestNoGenerateFilter)
Roses中,所有业务请求经过网关,网关做统一的鉴权,权限过滤,数据签名校验,并为每个请求生成该次请求的唯一请求号。唯一请求号的作用如下:请求在业务流转过程中可能经过多个微服务,查看这次请求的info日志信息,或者error日志信息等,需要从多个微服务的日志记录里去查找,效率非常低,那么,有了唯一请求号标识之后,可以用唯一请求号把请求经过的所有业务流转串起来,并存储起来,当请求遇到问题后,可以通过唯一请求号快速把这次请求的所有日志搜集并展示起来,从而方便排查问题。
Roses中,请求号在中转过程中填充到请求的Request-Header中,与之对应,响应时也会在Response-Hedaer中把本次的请求号输出。过滤器中的写法如下:
RequestContext currentContext = RequestContext.getCurrentContext();HttpServletResponse response = currentContext.getResponse();String requestNo = IdWorker.getIdStr();currentContext.addZuulRequestHeader(RosesConstants.REQUEST_NO_HEADER_NAME, requestNo);response.addHeader(RosesConstants.REQUEST_NO_HEADER_NAME, requestNo);
为了让Feign调用中,自动填充网关生成的唯一号,Roses增加了RosesFeignHeaderProcessInterceptor拦截器
4. 分布式事务解决方案(可靠消息最终一致性)
首先,分布式事务在不同业务场景下,解决方案是不一样的,时效性要求较高的场景下,例如订单支付成功后,更改订单状态,给用户账户加款,给积分账户加积分,三个操作在三个不同的服务下,这个时候可以用TCC方式解决事务问题;在时效性要求较为不严格下,例如订单支付成功后,需要异步录入会计凭证(不严格要求时效性),这个时候可以用可靠消息最终一致性解决。
Roses中实现了可靠消息最终一致性的解决方案(如上所说第二个例子),TCC方案目前未集成到系统。
实现原理
单纯依靠消息队列无法实现消息的可靠投递和消费,所以借助预发送待确认消息,业务执行成功后再发送确认消息来保证消息的可靠投递,并且通过中间服务(消息服务)来控制统一的消息预存储和确认发送,统一执行发送到消息队列的操作,并且消息服务有单独的消息表来记录消息是否已经投递成功。
Roses中roses-message-service为消息服务,为可靠消息最终一致性实现的核心,roses-message-checker为定时任务执行器,每隔一定时间来轮训消息表中是否有消息不一致的数据,若消息不一致则从业务系统中调用接口来查询具体业务的执行状态,从而来更新消息表中的消息。
roses-example-order和roses-example-account两个模块,模拟了分布式事务的场景,首先通过order模块下一个单(/place接口),再执行完成此订单(/finish接口),在完成订单过程中,先调用了预发送消息接口(preSaveMessage),之后执行完业务后调用确认并发送消息(confirmAndSendMessage)。在account模块中有消息的监听器,监听到消息后存储账号交易流水记录(recordFlow)。account和order模块为了演示业务流程用,数据库设计比较简单,不合理处请见谅。
//创建预发送消息ReliableMessage reliableMessage = createMessage(order);//预发送消息messageServiceConsumer.preSaveMessage(reliableMessage);//更新订单为成功状态(百分之50几率失败,模拟错误数据)(此处错误已添加到消息表的数据会被roses-message-checker轮询时删除掉)updateToSuccess(order);//确认消息messageServiceConsumer.confirmAndSendMessage(reliableMessage.getMessageId());
幂等性校验
消息的投递有重试机制,所以在消息的消费端需要加上幂等性校验,使得多次消费消息也可以让业务实际只执行一次,在account模块中幂等性的判断通过订单号来标识操作的唯一性。
5. 分布式配置中心roses-config
在Roses中,正如您所见,所有的模块都是在一个大工程下,但是实际日常开发中,模块往往分隔在多个项目中,每个项目有单独的小组来维护,小组与小组之间甚至代码都不是可见。当项目中的一些通用配置变动时,例如数据库地址,账号密码等,要么你通知各个小组修改他们的配置,要么你自己打开所有项目修改一遍。如果使用了分布式配置中心,可以把所有项目的配置收集起来,集中配置,那么你只需要打开配置中心的git仓库来修改配置,从而简化配置的维护工作。
除此之外,如果spring boot应用开启了actuator,配置仓库中的配置更改后,应用还可以通过/refresh动态刷新项目的配置,这在网关动态增加路由等配置上非常方便。
当然,开启分布式配置之后也有不完美的地方,在本地开发调试中,常常需要修改配置,直接修改远程仓库的配置又会影响到别人的本地开发,关于这种情况,Roses在每个项目的src/test/resources文件夹中,都保留了一个application-local.yml,这个配置文件是local环境下的应用的配置文件,您可以把它拷贝到src/main/resources中,并配置bootstrap.yml中的spring.cloud.config.enable配置为false,即可关闭配置中心的配置,使用本地的配置。
6. 一切请求基于RequestData和ResponseData
为了方便日常接口开发,Roses对控制器层的请求参数和响应进行了统一封装。所有post方式的请求,并且带有json请求body的都可以用RequestData类来作为参数接收请求数据,所有的响应都可以用ResponseBody来作响应的结果。
RequestData类中封装了对请求参数获取的常用方法,例如getString(),getInteger(),parse()等等,可以很方便的获取请求中包含的字符串数据,整型数据,以及解析请求为某个类。而ResponseData类中包含了对常用成功或者失败响应的封装,可以通过静态方法ResponseData.success()或者ResponseData.error()来响应成功的结果或者失败的响应。
例如,请求的数据是一段json:
{name:"order001