六. 服务层(后端)

6.1 概述

服务层(后端)是一个很大的概念,在云计算算,微服务,前后端分离的背景下,后端的服务形式其实是多样的,任何一个地址端点加端口都可以成为服务(TCP/UDP),在一般的应用,数据分析中,我们一般讨论的后端是指 web 应用的后端。

要完整的讨论后端技术是很困难的,我们先限定于web应用的后端,即,一般情况下,本文说的后端,是指向前端提供 RestAPI (也包括SOAP)服务的后端服务,关注于后端服务的实现与规范化。

6.2 编程语言

从现状出发,本文暂只讨论的编程语言为 java ,当然不可避免数据库交互会用到SQL。其他的编程语言, python 可用于特定场景如运维处理、数据挖掘计算,临时性的原型验证和数据格式转换处理等。JVM上的其他语言,如 Kotin,Scala,Groovy 在特定的场景下也可以使用,但出于通用性的考虑,不提倡; Golang 是当下比较流行的微服务开发语言之一,但在工业成熟度方面并非有那么大的优势,其主要优势可能在于并发性好,内存占用率低,对于我们目前做项目的定位来说,还不宜把Golang 作为主打语言,java 虽老,但仍生命力顽强,具有大量的新的特性未被普及使用。

  • 所有项目的JAVA 版本最低为 Java 8,除遗留项目外,新项目不得使用JDK8以下的版本,同时,在新的Java发布策略下,生产环境应当使用长期支持(LTS)版本,目前可接受的版本为 java 8,11,因此,请不要使用9,10版本

  • 除特殊情况下要用windows 下的本地服务交互外,所有后端的java组件部署,默认使用docker 打包,Linux 操作系统

  • 服务器环境使用64位的Java,不要再用 32位的

  • 开发测试过程中,请使用OpenJDK ,避免使用Oracle JDK ;代码中避免对于非标准JDK包的依赖(典型的如 com.sun 的包)

6.3 语言编程规范

java 语言的编程规范,直接参考《阿里巴巴Java开发手册》,当前版本为1.4版。下载查看(需要登录KM)在新窗口打开

需要强调的几点:

  • 代码风格默认为 java 8,因而推荐用新的 API,如 Stream , 新的DateTime ,Lambda 表达式。
  • 集合应当使用泛型指定类型。确实难以指定的,需要在java doc 中说明。
  • 除数据加工处理的逻辑外,禁止使用存储过程写业务逻辑。

6.4 系统开发框架

虽然 Spring 框架日益臃肿,但仍然可以算是java web 开发领域的事实标准。一般应用的开发,以SpringBoot 为开发脚本架起点。 我们后面会构建自己的 SpringBoot starter 。

SpringBoot 通过一系列的约定把一堆东西集成到一起,以快速把项目开发跑起来,但也还是有很多的选项的。部分场景下还有一些特性的功能,依赖项要处理,我们统一约定如下:

6.4.1 数据访问层(ORM)

  • 数据访问层(ORM) 一般情况下使用 Mybatis在新窗口打开,并配合 Mybatis Plus在新窗口打开使用
  • 特殊情况下,可以接受使用JPA,但不推荐,禁止项目中直接使用Hiberneta在新窗口打开
  • 数据分析类项目,因为只读的,复杂的值对象与条件较多,非报表工具的开发场景下,可以直接使用JDBC模板进行查询处理,但必须防止SQL注入

6.4.2 业务逻辑层

业务逻辑层作为开发时的主要代码编写处,却是最不好定义规范的,关键的强调:

  • 不要在MVC的Control中写业务逻辑。除了层次分工的职责原因外,关键是会破坏代码的重用性

  • 业务Service之间的互相调用时,注意控制事务的传播(请自补事务控制的逻辑)

  • 业务Service应当是无状态的,用户相关的信息,凭证应当以参数传入,Service中不得引用、处理任何与HTTP 相关的对象如Session,Request,Response

6.4.3 Web层(请求处理层)

Web层(请求处理层),特指http 请求到响应回去的转发、校验,控制过程。在前后端分享的背景下,View 由前端处理,后端只提供API,但是要考虑的是API给前端最终用户(浏览器,移动APP)或其他应用/平台调用。

  • 实现上, Web层的入口为 SpringMVC,不考虑其他的MVC框架。

  • 目前来说,Web层的接口提供方式为 RestFull 的API, 部分遗留系统要考虑SOAP的接口,长远来看,另一个需要关注的接口规范是GRAPHQL在新窗口打开

其他Web层需要关注的事项与路线:

6.4.3.1 SESSION 处理

微服务架构下,服务期望是无状态的,但是Web 层的前端API避免不了会话管理的问题。统一采用Spring Session ,将会话信息存储到 Redis 的方式存储会话信息。各服务组件的多个实例,共享同一个Redis库。应转变思路,减少在Session中存储过多的信息,不得在本地的内存中保存session。

对于后端服务的调用,使用 JWT在新窗口打开 认证。移动端APP访问后端服务,也推荐使用JWT在新窗口打开 作为认证凭证,对于提供给后端调用的API,不得有session 存在。

6.4.3.2 登录

当前的安全要求背景下,单纯的用户名密码验证总是不太符合安全要求的,同时成熟的企业应当都有统一的用户数据中心和单点登录体系。具体项目实施时,应当将单点登录作为首选项。甚至不对外提供单独的登录页面。

单独提供登录页面的,肯定需要满足密码安全性、复杂度,无效重试次数、验证码、双因素认证等要求。

6.4.3.3 单点登录

单点登录的首选方式是通过特定的HTTP 头传递用户ID,基于 HTTP 代理的单点登录解决方案大都是基于特定的HTTP头。以前最常用的 TAM ,CA 认证网关,基于Kong的API网关等,使用 iv_user, HTTP_REMOTE_USER,HTTP_X_AUTH_USER 等不同的HTTP头。这种单点登录模式下,需要控制网关入口的IP地址,不然就成为安全漏洞。

对于没有单点登录基础设计的客户,首选基于 API网关作为用户访问入口,在API网关上统一做认证处理。次选开源 CAS解决方案在新窗口打开

另一种常用的单点登录方式为OAUTH2.0在新窗口打开。严格来说 OAUTH 不是为了单点登录的标准。这一般就用在微信(企微)&钉钉集成中。

6.4.3.4 安全控制

对于API的访问也需要根据用户身份权限进行安全控制。基于 Spring Security 进行扩展以实现对API的权限控制 。

另一种场景是由API网关提供统一的访问入口的控制。API网关对服务调用者进行认证(包括前端用户/后端程序 ),但服务的提供者仍然要对网关传递过来的凭证进行判断。

企业应用场景下,对于后端的服务调用,应当统一记录审计日志。前端用户的操作,记录操作审计日志。

6.4.3.5 传统的SOAP WebService 提供与消费

确实有需要提供SOAP WebService 或调用SOAP的,统一用Apache CXF在新窗口打开 ,不要再使用 AXIS。

同时,SOAP WebService服务一般是提供给第三方应用(后端)调用的,建议与提供用户使用API的应用分开成为独立组件进行部署。

6.5 应用服务器与部署

既然使用微服务架构,用springboot 作为开发框架,那么传统的应用服务器的部署模式就没有必要了(当然应用服务器也是有其价值也是存在的,只是使用方式变了)。

  • 一般情况下,SpringBoot开发的服务,打包为Docker 镜像,用 java -jar 方式执行,不使用应用服务器(进一步完善控制JVM参数)

  • 特殊情况下需要将boot项目以war方式部署运行的,使用Tomcat ,tomcat 应当使用 8.5最新修订版本Tomcat 版本支持说明在新窗口打开

  • 新项目不应当再使用Websphere ,Weblogic之类应用服务器,使用k8s环境下的分布式编排,而不要用应用服务器的集群处理

6.6 公共服务与通用处理的约定

6.6.1 数据库连接池

SpringBoot 的应用中,连接池不再依赖于应用服务器的JNDI 连接池资源。自带连接池组件统一用 阿里Druid在新窗口打开

6.6.2 日志

因为SpringBoot 默认使用的LogBack ,为了省事,也统一用 Logback得了。log4j2 也是很出色的日志工具,只是对于我们来说,性能上的相关影响没那么重要。

需要注意的是,以前运行在应用服务器下时,日志一般不写入到 sysout 中,但在容器环境下,却推荐要将日志写入到 sysout 以方便于日志的查看与归集。

6.6.3 缓存

如前述,缓存中间件统一用Redis ,java 应用中连接 Redis的客户端是Jedis在新窗口打开,Jedis 提供了最完备的Redis功能操作API,但对于缓存来说,没有必要。

如果将 Redis 作为NoSQL数据库并启用持久化进行数据存储,可以用 Jedis 操作。 在缓存的场景中,我们统一使用 Spring Cache 抽象层接口,Spring Data Redis 提供的 RedisTemplate 进行交互操作。

  • Redis应当按场景划分 Database 的作用域,并设置访问密码,不得直接无密码开放
  • 推荐使用 Json String的序列化,而不要用JDK的序列化。因为实测String的序列化提供了更好的性能

6.6.4 附件存储

在不同的细分领域,所谓附件,应当有不同的存储处理方式的,典型如图片,视频流,会在存储与提取时进行相应的处理。这里所说的附件存储,只是先把附件当成二进制流看待进行存储,读取。

  • 不要将附件当成 Blob/Clob存储于关系数据库中,不要将附件存储到应用服务器的本地磁盘存储(不可集群扩展)
  • 统一使用 AWS S3在新窗口打开接口对接后端单独的对象存储服务,在数据库中存储附件元数据,对象存储存储附件内容。在具备条件的客户处,使用ceph集群在新窗口打开提供的对象存储服务,不具备条件的,用单独的VM安装minio存储在新窗口打开提供对象存储服务;公有云上当然用oss(阿里OSS也支持S3协议)
  • 定制化开发项目的最低要求也要使用NFS提供共享盘以存储附件内容

6.6.5 Office文档交互

Office文档的交互包括三部分:

  • 文档的解析导入

解析文档内容,形成结构化数据,常见于 excel 文件的数据上传,如果只是单纯的csv文本文件的解析,用 Apache Commons csv在新窗口打开方便够用,大数据处理场景中还可以考虑用 python 。在java 线上应用中,则统一用 Apache POI在新窗口打开,因为一些历史原因,POI项目有几年缺少维护,但当前的版本在功能方面已经远超出原 JXL/JexcelAPI在新窗口打开 之类项目。

  • 文档生成导出

统一用 Apache POI在新窗口打开,并只考虑导出 docx,xlsx 的open xml 文档格式。

  • 在线编辑,协作

在线编辑协作涉及到前端的范围。后端往往只提供内容流,或 WebDAV在新窗口打开 支持。而前端一般需要相应的商业化产品支持。

用于OA,合同等涉及到office 文件的查看、编辑、签章的,使用 NTKO Office在新窗口打开的产品,需要注意是否要电子签章有不同的版本。

在线的 office 文档编辑目前已经有一些较为成熟的商业解决方案,最好的当然是微软自家的Office Online Server(旧称 Office WebApp)在新窗口打开OnlyOffice在新窗口打开 也不错。但这不是我们的主营业务,因而技术路线不细关注。

6.6.6 定时任务

  • 定时任务作为单独的任务调度中心部署,不要与线上web应用放在一起,不要在给用户提供服务的组件中添加 quartz 之类的调度。因为定时任务是不能随着组件的集群化而随便多副本的

  • 推荐使用 XXl-JOB在新窗口打开单独部署作为多个定时任务调度的调度中心

  • K8s环境下,简单的任务调度可将任务打包为容器,使用 k8s 的CronJob 进行调试执行,但 CronJob 还不太适合于执行时间很短的(分钟以下)和多任务的精细控制,XXL-Job仍然是目前的首选

6.6.7 消息服务监听

消息服务监听被动监控一个消息队列(一般对应于PUB/SUB模式)。这种模式下,消息入口(监听者)一般是单实例的,消息接受后的处理根据情况可能并行。

  • 消息服务的监听处理,作为单独的部署组件,不要与用户API服务放在一起

6.6.8 全文检索

上次更新:
编辑者: 李贤伟