标签归档:Java

APP后台架构设计

项目背景&前期铺垫

2018年金融组织结构大调整,新的组织结构是中台研发。期初我对这个名称也感觉新鲜,了解后就发现,是连接前端部门与各业务部门桥接与提供公共服务的部门。
新组织架构调整伊始,开始对接各种项目,最繁琐的或许就是重前端的活动页搭建项目,也许因为这个原因吧,领导把我安排到了接收这个项目的“营销与权益”组。接手别人的项目总是痛苦的,以至于最后我们打算自己做一套。虽然很复杂,但是慢慢来应该不成问题。所以我决定全新用Spring boot2+,管理界面界面采用ElementUI做SPA,搭了一套还算比较完整的项目。总觉得以往的项目还是太过于陈旧,新项目就全部用最新的技术和框架重新搭建。小组成员们边了解需求边尝试,用了几周的时间做好了基础版的拖拽生成页面的Demo。当然没有专业前端的加入,界面还是比较丑陋的。

突然降临的挑战

上层领导决定公司APP大改版,时间很紧迫,任务非常重。原先交接过来的系统不能够支持新版APP的页面数据结构。虽然时间非常紧迫,从接到任务到上线预计也就一个月的时间,但考虑的需求比较简单还是决定全部重新做(后来发现,先期了解的需求并不全面,真是把自己给坑了)!有了上面“活动搭建”项目的后台,新项目进展还是比较迅速的。经过了解需求,思考了一下午,我大致想做一套基于领域模型驱动的架构(虽然由于自己才疏学浅,并没有在项目中把领域模型驱动设计发挥出来,但初衷还是有的),也就有了第一次小组会议我在墙上画下的初步架构设想图如图1:

第一次会议架构设想图

当然我想表达的是这样子的:

整体系统设计图

项目分析

作为APP后台的管理系统,主要就是操作APP每个原生页面的具体数据结构。要求各种数据动态配置,各种属性随意修改,APP在不进行版本升级的前提下,即时生效。简言之,也就是项目负责APP页面数据的下发,内容动态配置管理。而为了应对内容数据的可随意更改,可随时生效这个需求,我觉得Spring Express Language(后面统一称为SpEL)特别适合这个业务场景。所以,在系统里大量应用在页面数据调用接口动态取数据所需配置的地方,也成了整套系统的核心价值所在。

架构思考

这是一个典型的重读操作的系统,所以我就把项目分成了上面的两部分,一个后台管理,一个负责响应APP的接口请求返回页面结构数据。
因为Spring Boot2+是基于Spring5上集成而来的,Spring5是目前非常优秀且最新的Java应用框架,这个大版本增加了很多新的特性。比如:

  • JDK 基线更新
  • 核心框架修正
  • 核心容器更新
  • 含 Kotlin 在内的函数式编程
  • 响应式编程模型(重点)
  • 测试改进
  • 库支持
  • 中止支持

而基于Spring5上构建全新的系统,肯定会事半功倍的。具体详细架构如图

APP请求响应系统架构图(不包含配置后台)

整套系统有如下几个特点:

  1. 整个应对APP访问的CMS系统,不依赖于MySQL数据源,所有页面基础数据均读Redis缓存数据。
  2. 可封装或不封装业务接口,根绝业务场景复杂度而定。一切以页面配置简易化为主。所有所需要动态配置数据结构的模板元素配置项,都用EL表达式来代替。当然这个方式大大提高了动态灵活性,但是加大了运维人员的门槛高度,所以,目前为止,第一版上线的所有表达式配置项,均有我们研发自己配上去的。
  3. 业务接口熔断处理,基于Hystrix的熔断器与方法降级处理,做页面请求接口托底方案。另一个托底方案,是运营可以配置多个相同页面模板,根据用EL表达式描述的显示规则来做模板托底方案。
  4. 为了提升系统性能,主要做了如下几点优化处理:
    1. 页面全局接口基于正则匹配的表达式抽取,相同方法参数统一请求处理,将请求结果保存在Spring EL渲染模板的上下文中作为结果变量,将原先配置的表达式替换为从结果变量中提取结果数据。这样就可做到:一个接口相同的方法,只要参数一致,无论你在页面中配置了多少处表达式,在单次渲染整个页面周期时,只做一次调用。大大提升了性能,降低了延迟。
    2. 异步结果请求。上面所有需要调用接口取数据的表达式,均被分配到了单独一个线程去请求结果,其实保存在上下文中的环境变量就是封装结果的Future。页面渲染时,会先分配N个线程去请求数据,等到后续真正组装结果时,才会去调用get阻塞获取结果。当然这样做,提升速度的代价就是极大消耗CPU性能,因为线程池需要设置的比较大,而具体设置为多大,还要在不断试验的基础上不断优化调整,以达到服务器最优效能和最大并发支持。
    3. 基于Actor模型Reactor响应式处理页面数据。页面数据是有规则的,也就是多个模板集合而组合起来的,而每个模板里又是一个元素集合,每个元素里又是一个属性集合,是个比较规范的数据结构,虽然最大的坑在于层级深度不固定,但是已经用JSON去填了这个坑。而针对这个相对规范的数据层级结构,用响应式编程(流式处理)再合适不过了。So,一堆Flux使用和Subscriber飘过…
    4. 数据读取走JVM。速度还不到极致,虽然不读MySQL,但是每次构建页面数据,读多次Redis的网络开销,我也是不想耗费的。So,基于Guava的LoadingCache构建Jvm本地缓存方案就在优化系统的路上走马上任了!至于有人问为什么不基于HashMap的本地静态变量做?仅仅是为了实现的更加优雅!当然用HashMap也是完全可以做的,只是我却是一定程度上喜欢用牛刀。

项目开发

项目的研发进度还是很快的,由于时间紧迫,各业务方提供的接口参差不齐,千奇百怪。这也是我们没有上来就做接口规范所挖的坑。

  1. 我们有一个很大的工作量就是重新封装各业务方接口,包装成自己所负责页面方面用表达式SpEL描述的样子。而这些工作的确本不该有我们部门的研发去做的。
  2. 其次有个很大的工作量就是SpEL表达式在各业务页上面的配置,原本我以为这是最简单快捷的工作。但是由于其他成员没有了解过,导致出错不断。再就是验证表达式正确性,需要到预发环境验证,因为没有测试环境,对工作效率造成了很大的影响。后来,为了应对这个SpEL验证表达式正确与否的问题,专门开发了一个测试接口,才一定程度上解决了这个问题。其实这个验证接口确实该早些准备好的,的确是我的失职。

回归分析

为了应对需求的动态可配置,采取了页面配置里各种SpEL表达式。造成最大的一个问题就是,运营不爽了!
    – “这都是啥呀?完全看不懂!”
    – “稍微一改,页面就崩了”
    – “完全不敢动啊,一动就坏!”
各式各样的的吐槽声接憧而至。表现了运营对我们系统易用性的各种不满,我也是一脸无奈。的确,表达式就是代码,把最抽象的东西摆在他们眼前,看不懂都是次要的,他们稍微改动一个标点符号的确就会造成异常!
可是,他们是用户,无论如何得满足啊!为此,我也时常反思,系统还能怎么优化?如果再让我重新实现一遍呢?
对此列举系统优化点:

  1. 表达式配置,改为用户选择方法,让SpEL表达式在用户看不到的地方动态生成。(这点,组里优秀的小伙伴已经基本实现。是先配置在系统专门维护的方法表里,然后供页面选择)当然,我也在思考,如何通过注解,反射等机制把这个事情自动化,不要要挨个去录入。我觉得如果有机会,可以这样去做吧!
  2. 规则进一步抽象独立出来,用来适应更复杂的场景和更复杂度规则执行。
  3. 尽量剥离系统中业务接口的直接引用,保持系统轻量化。完善RPC动态注册配置,避免jar包引入方式。

其实还有好多需要优化的点,却有难以一时总结清晰,后续更新吧…

系统已经灰度发布,截至目前为止,报警全部来自业务方接口调用失败,异常。当然,后续的大流量能否抗住,还要持续观测与优化,也是对我最大的考验。

未完…待续…

Java垃圾回收机制详解

对于Java垃圾回收机制这个问题,一直是Java招聘面试津津乐道的一个问题之一。就好像答不上此问题就证明你不够NX一样。在此将这个问题做一下整理,以备后人方便。

Java语言中一个显着的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制, Java中的对象不再有”作用域”的概念,只有对象的引用才有”作用域”。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。 继续阅读

Java模拟Http访问

Java模拟Http访问将返回的JSON解析为Bean对象

应用场景:某系统访问另一个系统获取一些数据,其中这些返回数据类型必须是JSON格式,接受系统可以将结果转换为简单的Bean对象,也可以将返回的是集合数据转换成List。这就需要我们模拟一个Http访问,然后处理返回的JSON数据。

方案选型:看了看网上的很多Demo,都是比较老的。其中应用的apache的框架现在也已经找不到了。所以决定亲自去apache官网去看看。发现以前的httpclient包已经独立出来命名为Apache HttpComponents。当前最新包为4.3.3。我采用的是OSChinaMaven源,还不错,已经有资源了。而JSON解析才用了最为流行的Jackson

Demo示例:

Test.java 返回的JSON数据将要被解析成为的对象类 继续阅读

Annotation与SpEL实现系统记录操作日志

先说几句废话吧。最近工作比较混乱,一遍研究着Hadoop,一遍搞着另外一个系统的开发。这段时间,一心想写点技术文章却迟迟没能提笔。今天终于,打开音乐播放器,戴上耳机。享受着宁静的夜晚与指尖跃起的文字。甚至于还想着,什么时候才能有合适的机会回到山东,守在爸妈身边。好了,废话不多说了,开始记录正文。

需求:

系统中的一个模块属于关键区,它所有的操作主要针对修改与删除是要求记录下日志来的。而这个记录的日志并不是像我们把它们打印在log文件里,而是需要标准的记录到数据库中。以便于后来专门日志操作模块的查询。

思考: 继续阅读

了解一下JMS

1. JMS基本概念

(1)JMS(Java Message Service)是访问企业消息系统的标准API,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发。(2)Java消息服务(Java Message ServiceJMS)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。Java消息服务是一个与具体平台无关的API,绝大多数MOM提供商都对JMS提供支持。 

2. JMS基本功能

JMS是用于和面向消息的中间件相互通信的应用程序接口。它既支持点对点(point-to-point)的域,又支持发布/订阅(publish/subscribe)类型的域,并且提供对下列类型的支持:经认可的消息传递,事务型消息的传递,一致性消息和具有持久性的订阅者支持。JMS还提供了另一种方式来对您的应用与旧的后台系统相集成。Java消息服务的规范包括两种消息模式,点对点和发布者/订阅者。许多提供商支持这一通用框架因此,程序员可以在他们的分布式软件中实现面向消息的操作,这些操作将具有不同面向消息中间件产品的可移植性。

Java消息服务支持同步和异步的消息处理,在某些场景下,异步消息是必要的;在其他场景下,异步消息比同步消息操作更加便利。

Java消息服务支持面向事件的方法接收消息,事件驱动的程序设计现在被广泛认为是一种富有成效的程序设计范例,程序员们都相当熟悉。 继续阅读

Java实现按位截取字符串

好久没有写点什么了。原因很简单,忙!近三个月几乎天天加班,基本没有消停过。工作的强度很大,但是没有压力;可是生活的强度并不大,却充斥的满满的。不想开篇就是牢骚满腹,但毕业生被作为廉价劳动力的滋味并不是很好受。今年我终于不再是应届毕业生了,话说我可以作为一个社会招聘去跳槽了吧?
今天要说的一个例子也是我在网上没有找到,而费了很长时间自己解决的。
 例子:
一个字符串:”12345ABC陈陈陈陈12″
使用Java中substring(5,10) = “ABC陈陈” 。而substring(10,12) = “陈陈”,并且它是从第2个陈开始截取的。如果我用substring(5,16),应该会报错的。因为它是按照字符去截取的。但是,我不能按照字符去截取,我需要按照字节来。也就是说我期望的结果是:substring(5,10) = “ABC陈” substring(10,12) = “陈” 而它应该是从第一个陈开始截取的。substring(5,16) = “ABC陈陈陈陈”,似乎Java的方法中没有实现我这个需求的。
所以我只能自己想办法截取它。
 需求:
一个长长的字符串,我要根据定义文件中的每个字段的位数去从头开始截取他们,然后一个个的赋值到字段中去。
思路:
从头开始截取,截完一段后,改变原字符串(即去掉截掉的部分)。然后对待上述文件中的全角字符的时候循环截取,直到截取到定义的长度为止。

继续阅读

递归查询森林结构所有子节点

近期工作上遇到了这样一个需求,一张表用来存储树形结构的父节点和叶子节点,大体是这样定义的。

Table定义如下:

id groupid nodeid other
1 A001 A0001 ~~
2 B001 B0001 ~~
3 A0001 A00001 ~~
4 C001 C0001 ~~
5 C001 C0002 ~~

需求很简单:这张表表示的是一个数结构,其中groupid代表的是父节点,而nodeid代表的是相应的子节点,我们想要的是整个树结构的全部叶子节点。当然涉及到多棵树,就组成了森林结构,也就是查找指定森林结构的所有叶子节点。

思路也很Easy:遍历每一颗树,利用写好的递归查询函数去查询每棵树的最终叶子结点,然后把它们保存到静态list中。

详细代码如下: 继续阅读

The Java™ Programming Language SL-275 Student Workbook Answer

Object-Oriented Programming Test

这是一个关于银行(Banking)的Java练习题,很多时候用来作为Java入门的教材。我看了看确实不错,从最简单的Activity慢慢的演变,一直到最后都贯穿了整个Java的知识点。

但是在我寻求答案的时候并没有顺利的找到这个联系的答案,所以我特别上传了一下,以方便别人来索引。

答案下载:

SL275LABwithANSWER

如果下载有错误,请及时联系我:me[at]chenzhiguo.cn

Java模拟标准双色球

近期一直在复习Java基础知识,确实不仅补缺查漏,还能增强记忆。同事提了个需求想要模拟一下双色球的输出,顺便练习一下刚学的Collection类。反正闲的没事,就决定练习一下。

一开始我想用Set最为简便,因为其自身值的唯一性,可以很好地去除随机值重复的问题。所以,一会儿就用Set写了下面这个版本。

 

继续阅读

最新S2SH框架基于Annotation的整合开发(原作)

因为自己学习的时候,资料过旧,困难重重。所以再足以单独开发后,写出来让后来人学习能够轻松点。
S2SH(Struts2+Spring+Hibernate)框架是当今最流行的Java企业级开发框架搭配方案之一。S2SH是SSH的升级版本,2代指的是Struts2,而SSH中用的是Struts1。众所周知,Struts1已经在逐渐退出舞台。而Struts2也就是Webwork的衍化版本,正在如火如荼的盛行着。
其实网上这方面资料也不算少,但是都知道现在技术发展的太快,旧的资料已经很难解决我们的当前问题,要问我写的这些有多新?我学习S2SH框架是在2011年8月分暑假期间,这篇文章写于2011年8月15日星期一。
我所采用的是当前位置,各大官网发布的最新框架。Struts2.2.3,Spring2.5.6和Hibernate3.6.6Final。
首先我们需要准备什么?
MyEclipse for Spring 8.6或者更新
Struts2.2.3包或者更新
Spring2.5.6包或者更新
Hibernate3.6.6包或者更新
继续阅读