搭建一个新的单体框架

搭建一个新的单体框架

架构的演进

软件架构风格从大型机(Mainframe),到原始分布式(Distributed),到大型单体(Monolithic),到面向服务(Service-Oriented),到微服务(Microservices),到服务网格(Service Mesh),到无服务(Serverless)……技术架构上确实呈现出“从大到小”的发展趋势。当近年来微服务兴起以后,涌现出各类文章去总结、赞美微服务带来的种种好处,诸如简化部署、逻辑拆分更清晰、便于技术异构、易于伸缩拓展应对更高的性能等等,这些当然都是重要优点和动力。可是,如果不拘泥于特定系统或特定某个问题,以更宏观的角度来看,前面所列这种种好处却都只能算是“锦上添花”、是属于让系统“活得更好”的动因,肯定比不上系统如何“确保生存”的需求来得关键、本质。笔者看来,架构演变最重要的驱动力,或者说这种“从大到小”趋势的最根本的驱动力,始终都是为了方便某个服务能够顺利地“死去”与“重生”而设计的,个体服务的生死更迭,是关系到整个系统能否可靠续存的关键因素。

举个例子,譬如某企业中应用的单体架构的Java系统,其更新、升级都必须要有固定的停机计划,必须在特定的时间窗口内才能按时开始,必须按时结束。如果出现了非计划的宕机,那便是生产事故。但是软件的缺陷不会遵循领导定下的停机计划来“安排时间出错”,为了应对缺陷与变化,做到不停机地检修,Java曾经搞出了OSGi和JVMTI Instrumentation等这样复杂的HotSwap方案,以实现给奔跑中的汽车更换轮胎这种匪夷所思却又无可奈何的需求;而在微服务架构的视角下,所谓系统检修,不过只是一次在线服务更新而已,先停掉1/3的机器,升级新的软件版本,再有条不紊地导流、测试、做金丝雀发布,一切都是显得如此理所当然、平淡寻常;而在无服务架构的视角下,我们甚至都不可能去关心服务所运行的基础设施,连机器是哪台都不必知道,停机升级什么的就根本无从谈起了。

流水不腐,有老朽,有消亡,有重生,有更迭才是正常生态的运作合理规律。请设想一下,如果你的系统中每个部件都符合“Phoenix”的特性,哪怕其中某些部件采用了由极不靠谱的人员所开发的极不靠谱程序代码,哪怕存有严重的内存泄漏问题,最多只能服务三分钟就一定会崩溃。而即便这样,只要在整体架构设计有恰当的、自动化的错误熔断、服务淘汰和重建的机制,在系统外部来观察,整体上仍然有可能表现出稳定和健壮的服务能力。

构思系统架构

系统设计的目的->系统设计的目标->围绕目标的核心设计->围绕核心设计形成的设计原则->各子系统,模块的详细设计。

基本上应用逻辑架构的推导有4个子路径,他们分别是:

  1. 业务概念架构:业务概念架构来自于业务概念模型和业务流程;
  2. 系统模型:来自于业务概念模型;
  3. 系统流程:来自业务流程;
  4. 非功能性的系统支撑:来自对性能、稳定性、成本的需要。

系统设计的目的

这个系统设计主要是为了解决定制服务商为用户定制程序单台服务器仅可为五个机构服务而出发设计的,设计时应尽量使架构简洁又稳定具备有高拓展性,在其基础上能够快速的二次开发,需要考虑功能的实用性以及对资源的利用率,并能够承受一定的访问量。设计应考虑可能部署在用户私人服务器上等多项因素。应考虑后期重构和演进所带来的问题。

目前会移植的模块:

招生系统,教育机构预约课时,打卡签到等功能

需要集成钉钉交互,实现通讯录同步,实现数据权限控制,钉钉打卡,消息通知模块,钉钉审批流

系统设计的目标

系统可在1h 2g的服务器上运行

系统内存占用大致在200M

可快速迭代

基础的系统抵御攻击能力

超预期流量控制等

等等后期完善,暂时对系统的目标没有太清楚的衡量

技术选型

1、系统环境

  • Java EE 8
  • Servlet 3.0
  • Apache Maven 3

2、主框架

  • Spring Boot 2.3.2
  • Spring Framework 5.2.8
  • Apache Shiro 1.5.3

3、持久层

  • Apache MyBatis 3.5.4
  • Alibaba Druid 1.1.23
  • Redis 2.3.2

4、视图层

  • Vue 2
  • Swagger UI 3.0

待整合模块

websocket

spring-boot-demo-ratelimit-guava

https://github.com/wytalw/spring-boot-demo/tree/master/spring-boot-demo-ratelimit-guava

CIDI

寻找稳定的spring Boot版本

系统需求

  • JDK >= 1.8
  • MySQL >= 5.7
  • Maven >= 3.0

GA:General Available:通用可使用,官方推荐使用此版本

PRE:预览版,内测版

SNAPSHOT

RC:Release

稳定性(由小到大):

PRE<SNAPSHOT<RC<GA

使用websocket模块

内置功能

  1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
  2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。
  3. 岗位管理:配置系统用户所属担任职务。
  4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
  5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。
  6. 通知公告:系统通知公告信息发布维护。
  7. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
  8. 系统接口:根据业务代码自动生成相关的api接口文档。
  9. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。
  10. 消息通知:发送消息通知到钉钉
  11. 考勤打卡:同步钉钉考勤打卡
  12. 钉钉数据交互
  13. 钉钉审批流
  14. 文件上传

ehcache 和Redis的选择

1. Ehcache简介

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认CacheProvider。Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。

Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Ehcache),但本身不直接提供缓存功能的实现。它支持注解方式使用缓存,非常方便。

Ehcache的特点:

  1. 快速
  2. 简单
  3. 多种缓存策略
  4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题
  5. 缓存数据会在虚拟机重启的过程中写入磁盘
  6. 可以通过RMI、可插入API等方式进行分布式缓存
  7. 具有缓存和缓存管理器的侦听接口
  8. 支持多缓存管理器实例,以及一个实例的多个缓存区域
  9. 提供Hibernate的缓存实现

2.Redis简介

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。

Redis是一个key-value存储系统。它支持存储的value类型很多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。Redis将数据都是缓存在内存中。Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis的特点:

1、速度快 

Redis是用C语言实现的; Redis的所有数据存储在内存中。 

2、持久化 

Redis的所有数据存储在内存中,对数据的更新将异步地保存到磁盘上。 

3、支持多种数据结构 

 Redis支持五种数据结构:String、List、Set、Hash、Zset 

4、支持多种编程语言 

Java、php、Python、Ruby、Lua、Node.js 

5、功能丰富 

除了支持五种数据结构之外,还支持事务、流水线、发布/订阅、消息队列等功能。 

6、源码简单 

约23000行C语言源代码。 

7、主从复制 

主服务器(master)执行添加、修改、删除,从服务器执行查询。 

8、高可用及分布式 
Redis-Sentinel(v2.8)支持高可用 。Redis-Cluster(v3.0)支持分布式

3. Ehcache 和 Redis 比较

Ehcache Redis
存取速度 Ehcache直接在jvm虚拟机中缓存,速度快,效率高 Redis是通过socket访问到缓存服务,效率比ecache低
集群和分布式 Ehcache有缓存共享方案,不过是通过RMI或者Jgroup多播方式进行广播缓存通知更新,缓存共享复杂,维护不方便;简单的共享可以,但是涉及到缓存恢复,大数据缓存,则不合适。 Redis有成熟的分布式解决方案。适合大规模分布式集群部署。
操作复杂度 Ehcache提供的接口非常简单明了,从Ehcache的搭建到运用运行仅仅需要的是你宝贵的几分钟。其实很多开发者都不知道自己用在用Ehcache,Ehcache被广泛的运用于其他的开源项目。比如:Hibernate 至少需要安装服务端和客户端才能使用。操作略比Ehcache复杂一些。

资料来源:https://blog.csdn.net/ShiXueTanLang/article/details/79596198

基于shiro+jwt的真正rest url权限管理,前后端分离

img

swagger设置全局token,解决接口需要token验证的问题

系统流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
st=>start: 开始
e=>end: 登录
io1=>inputoutput: 扫码登陆/免密登陆
sub1=>subroutine: 是否有此用户
cond=>condition: 是否有此用户
cond2=>condition: 密码是否正确
op=>operation: 读入用户信息


st->io1->sub1->cond
cond(yes,right)->cond2
cond(no)->io1(right)
cond2(yes,right)->op->e
cond2(no)->io1

jwt

1.为什么要使用jwt

答:因为钉钉特殊的开发环境,并且我们的开发模式是前后端分离的开发模式,所以没法使用cookie+session的解决方案。在一定的场景下cookie+session相对于jwt更节省资源。

JWT 虽然对于大多数网站都没有用,但是有几种情况它是很有用的。

如果你正在构建从服务器到服务器或客户端到服务器(如:移动应用 APP 或单页面应用)的 API 服务,那么使用 JWT 是非常明智的。比如:

  • 你的客户端需要通过 API 进行身份验证,并返回 JWT
  • 然后,客户端使用返回的 JWT 经过身份验证去请求其它的 API 服务
  • 这些其它 API 服务通过客户端的 JWT 验证客户端是可信的,并且可以执行某些操作无需再次验证

具体请看 https://blog.csdn.net/weixin_41153791/article/details/82291144

2.jwt是什么?

答:普通的oauth2颁发的就是一串随机hash字符串,本身无意义,而jwt使用一种特殊格式的token,token是有特定含义的,分为三部分:
-头部Header
-载荷Payload
-签名Signature
这三部分均用base64进行编码,并使用.进行分隔。一个典型的jwt格式的token类似xxxxx.yyyyy.zzzzz。

jwt其实并不是什么高深莫测的技术,相反非常简单。认证服务器通过对称或非对称的加密方式利用payload生成signature,并在header中申明签名方式,仅此而已。通过这种本质上极其传统的方式,jwt可以实现分布式的token验证功能,即资源服务器通过事先维护好的对称或者非对称密钥(非对称的话就是认证服务器提供的公钥),直接在本地验证token,这种去中心化的验证机制非常适合分布式架构。jwt相对于传统的token来说,解决以下两个痛点:
1、通过验证签名,对于token的验证可以直接在资源服务器本地完成,不需要连接认证服务器;
2、在payload中可以包含用户相关信息,这样就轻松实现了token和用户信息的绑定;
如果认证服务器颁发的是jwt格式的token,那么资源服务器就可以直接自己验证token的有效性并绑定用户,这无疑大大提升了处理效率且减少了单点隐患。

适用场景:
一次性的身份认证、api的鉴权等,这些场景能充分发挥jwt无状态以及分布式验证的优势。

不适用的场景:
不要试图用jwt去代替session。这种模式下其实传统的session+cookie机制工作的更好,jwt因为其无状态和分布式,事实上只要在有效期内,是无法作废的,用户的签退更多是一个客户端的签退,服务端token仍然有效,你只要使用这个token,仍然可以登陆系统。另外一个问题是续签问题,使用token,无疑令续签变得十分麻烦,当然你也可以通过redis去记录token状态,并在用户访问后更新这个状态,但这就是硬生生把jwt的无状态搞成有状态了,而这些在传统的session+cookie机制中都是不需要去考虑的。

3.什么情况下不适用oauth2 而选择jwt

OAuth2中使用token验证用户登录合法性,但token最大的问题是不携带用户信息,资源服务器无法在本地进行验证,每次对于资源的访问,资源服务器都需要向认证服务器发起请求,一是验证token的有效性,二是获取token对应的用户信息。如果有大量的此类请求,无疑处理效率是很低,且认证服务器会变成一个中心节点,这在分布式架构下很影响性能。
JWT就是在这样的背景下诞生的,从本质上来说,jwt和OAuth2没有可比性。

4.使用jwt的好处

1.支持跨域访问:Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输
2.无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息
4.更适用CDN:可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可(居于前面两点得出这个更适用于CDN内容分发网络)
5.去耦:不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可
6.更适用于移动应用:当你的客户端是一个原生平台(iOS,Android,Windows等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多
7.CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范(如果token是用cookie保存,CSRF还是需要考虑,一般建议使用①在HTTP请求中以参数的形式加入一个服务器端产生的token。或者②放入http请求头中也就是一次性给所有该类请求加上csrftoken这个HTTP头属性,并把token值放入其中)
8.性能:一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算的Token验证和解析要费时得多
9.不需要为登录页面做特殊处理:如果你使用Protractor做功能测试的时候,不再需要为登录页面做特殊处理
10.基于标准化:你的API可以采用标准化的JSON Web Token(JWT)这个标准已经存在多个后端库(.NET,Ruby,Java,Python,PHP)和多家公司的支持(如:Firebase,Google,Microsoft)

5.鉴权流程 使用jwt做最外层拦截

img

具体参照 https://www.jianshu.com/p/0b1131be7ace

  1. shiro鉴权方式

img

7.从权限控制到shiro框架的应用

https://www.jianshu.com/p/1ce12657abc5

https://blog.csdn.net/weixin_34204057/article/details/94628341

8.shiro相关认证

https://www.jianshu.com/p/0b1131be7ace

https://blog.csdn.net/qq_28336351/article/details/83249242

https://blog.csdn.net/u013283727/article/details/79367001

http://www.51gjie.com/javaweb/1134.html

http://www.51gjie.com/javaweb/1141.html

http://www.51gjie.com/javaweb/1136.html

接下来我们再分别用实例给大家介绍下Iass、Sass、Pass

iaas paas saas三种云服务

第一层叫做IaaS

IaaS:Infrastructure-as-a-Service(基础设施即服务)

举例:几年前如果你想在办公室或者公司的网站上运行一些企业应用,你需要去买服务器,或者别的高昂的硬件来控制本地应用,才能让你的业务正常运行。

但现在可以租用IaaS公司提供的场外服务器,存储和网络硬件。这样一来,便大大的节省了维护成本和办公场地。比如知名的IaaS有Amazon,Microsoft和阿里云、百度云、美猴云等。

第二层就是所谓的PaaS

PaaS:Platform-as-a-Service(平台即服务)

举例: PaaS公司在网上提供各种开发和分发应用的解决方案,比如虚拟服务器和操作系统。这节省了你在硬件上的费用,也让分散的工作室之间的合作变得更加容易。网页应用管理,应用设计,应用虚拟主机,存储,安全以及应用开发协作工具等。Google App Engine, Salesforce的force.com平台,八百客的800APP是PaaS的代表产品。

第三层也就是所谓SaaS

SaaS:Software-as-a-Service(软件即服务)

举例:生活中,几乎我们每一天都在接触SaaS云服务,比如:我们平时使用的苹果手机云服务,网页中的一些云服务等。

Not registered via @EnableConfigurationProperties, marked as Spring component, or scanned via @ConfigurationPropertiesScan

按照提示点击跳转到官方文档,接着在pom.xml中添加如下的配置

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

添加完后的效果是,当你写配置文件(yml,properties配置文件)时会有相关的提示

添加后错误确实是没了,但是在SpringBoot的单元测试时会看到如下的错误:Could not autowire. No beans of ‘Person’ type found

回到自定义的bean Person中,添加注解@Component,声明将这个组件添加至容器中,这样才可以被使用?

“只有这个组件是容器中的组件,才能使用容器提供的@ConfigurationProperties功能,”

1
2
3
如果发现@ConfigurationPropertie不生效,有可能是项目的目录结构问题,可以通过@EnableConfigurationProperties(ConnectionSettings.class)来明确指定需要用哪个实体类来装载配置信息。

Spring boot 1.5以上去除了location属性,可采用@Component的方式注册为组件,然后使用@PropertySource来指定自定义的资源目录。

为什么要用到消息队列,什么时候适合用消息队列?

什么时候使用消息队列

同步需求,远程过程调用(PRC)更适合你。

异步需求,消息队列更适合你。

目前很多消息队列软件同时支持RPC功能,很多RPC系统也能异步调用。

消息队列用来实现下列需求

  1. 存储转发
  2. 分布式事务
  3. 发布订阅
  4. 基于内容的路由
  5. 点对点连接

什么场合使用消息队列

你首先需要弄清楚,消息队列与远程过程调用的区别,在很多读者咨询我的时候,我发现他们需要的是RPC(远程过程调用),而不是消息队列。

消息队列有同步或异步实现方式,通常我们采用异步方式使用消息队列,远程过程调用多采用同步方式。

MQ与RPC有什么不同? MQ通常传递无规则协议,这个协议由用户定义并且实现存储转发;而RPC通常是专用协议,调用过程返回结果。

消息队列最常的使用场景:解耦,异步,削峰。

租户ID的获取(根据业务场景判断隔离的租户)

模块注意事项

Swagger3.0.0版本多了一个dataTypeClass属性,默认值是Void.class,接口参数建议指定该属性,否则项目启动时会有不少数据类型无法解释的警告:

  1.         @ApiImplicitParam(name = "name", value="姓名", required = true, dataTypeClass = String.class),
    

总结

框架架构失败,主管接手重构。在设计的时候整个框架的搭建都急于求成,没有考虑到框架的客观实用性,我们的业务主体是和钉钉体系结合,并不适用于这种通用速成框架,以至于采用框架后,对整个框架基本进行了重构,留下的核心部分其实也只剩下了日志模块和security模块,其他都被重构了,并且工程量远大于重新写一个新的项目,并且项目存在太多冗余代码,对此吃了一个没有经验的亏。非常的难受。在这次的架构学习中,对jwt的token验证以及token生成流程有了一定的了解,对security的认证流程也有一定的了解,并且在重构oss和支付模块的时候,有了非常多对于别人代码的接触,觉得别人代码写得很优秀,学习到了我从没运用的写法,也加深了我对设计模式的感触,也开始了我的设计模式学习之路。希望学习后能在我后续的模块中不断加深对设计模式的认识。也第一次认识到了租户ID以及sql拦截器,增加了对mybatis plus的使用在后续的学习中应该加强对mybatis源码的学习,对mybatis底层个人还存在知识盲区,以至于在很多情况中出现认知不清的情况。

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码

请我喝杯咖啡吧~

支付宝
微信