SpringBoot默认的日志配置通常来说足够满足要求:日志记录到控制台,也能够配置日志的级别,样式等等。但是其也有些不足:没有了之前logback提供的热更新日志配置的功能。
logback有个非常好用的配置是“scan”,当“scan=true”的时候默认一分钟刷新一次配置。这样可以自动更新系统的日志级别。对于在线系统可以方便修改日志级别获取更加详细的日志,方便定位错误。这样可以保证服务不中断的情况下调整日志级别获取更多日志信息。但是默认的SpringBoot配置中不支持这种选项。
与子偕老
SpringBoot默认的日志配置通常来说足够满足要求:日志记录到控制台,也能够配置日志的级别,样式等等。但是其也有些不足:没有了之前logback提供的热更新日志配置的功能。
logback有个非常好用的配置是“scan”,当“scan=true”的时候默认一分钟刷新一次配置。这样可以自动更新系统的日志级别。对于在线系统可以方便修改日志级别获取更加详细的日志,方便定位错误。这样可以保证服务不中断的情况下调整日志级别获取更多日志信息。但是默认的SpringBoot配置中不支持这种选项。
Redis中的发布订阅机制(Pub/Sub)是基于channel这一概念的,这有些类似于Kafka中的基于topic的消息机制,只是不支持持久化。如果publish的消息,没有任何client处于"subscribe"状态,消息将会被丢弃。如果client在subcribe时,链接断开后重连,那么此期间的消息也将丢失。Redis server将会"尽力"将消息发送给处于subscribe状态的client,但是仍不会保证每条消息都能被正确接收。
为了解耦发布者(publisher)和订阅者(subscriber)之间的关系,Redis 使用了 channel (频道)作为两者的中介:发布者将信息直接发布给 channel ,而 channel 负责将信息发送给适当的订阅者,发布者和订阅者之间没有相互关系,也不知道对方的存在。
Spring Data Redis组件对Pub/Sub机制进行了抽象,提供了类似JMS的编程模式。Spring Data Redis使用了一个Container(RedisMessageListenerContainer)来解决发布订阅机制。这个Container使用一个Redis链接解决了多个topic订阅的问题。它把其他的订阅、发布者隔离成基本的POJO对象,而不用与Redis对象打交道。这简化了整个编程模型。
随着技术的发展,现在的Web系统架构基本都由原来的后端渲染(例如JSP),变成了:前端渲染、前后端分离的形态。前端和后端的唯一联系,变成了API接口。API文档变成了前后端开发人员联系的纽带,变得越来越重要,swagger就是一款让你更好的书写API文档的框架。没有API文档工具之前,大家都是手写API文档的,在什么地方书写的都有,有在Wiki上写的,有在Word里面写的,也有在对应的项目目录下readme.md上写的,每个公司都有每个公司的玩法,无所谓好坏。另外还有专门针对API文档开发的应用,例如showdoc之类的。
书写API文档的工具有很多,但是能称之为“框架”的,估计也只有swagger了。使用Swagger有个好处就是文档与代码结合紧密,更新代码的同时也会更新文档,不用担心同步的问题。而且在Java中使用Swagger也比较方便。
云认证平台提供的Restful接口是基于CXF实现的。网上现有的关于Swagger的文档多数是基于Spring MVC的,并不适用于CXF。下面我将针对CXF+SpringBoot的组合如何使用Swagger进行介绍。
数据库中有一个bigint类型数据,对应java后台类型为Long型,在某个查询页面中碰到了问题:页面上显示的数据和数据库中的数据不一致。例如数据库中存储的是:1475797674679549851,显示出来却成了1475797674679550000,后面几位全变成了0,精度丢失了。
上篇文章介绍了一个方法,解决了导出Excel过程中内存溢出的问题。但是有些问题还没有搞明白,那就是为什么要这样打开数据库游标?为什么fetchSize必须为:-2147483648?这里面涉及到数据库游标的更多信息,这一篇文章将深入介绍这方面内容。
在数据库中,游标是一个十分重要的概念。游标提供了一种对从表中检索出的数据进行操作的灵活手段,就本质而言,游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。游标总是与一条SQL 选择语句相关联因为游标由结果集(可以是零条、一条或由相关的选择语句检索出的多条记录)和结果集中指向特定记录的游标位置组成。
MySQL数据库游标通常有两种形式:Client Side Cursor(客户端游标)和Server Side Cursor(服务器端游标)。默认情况下,客户端游标会把整个结果集获取到客户端内存中,如果结果集太大,就会引发Out Of Memory错误;而服务器端游标会将结果集缓存在服务器端,客户端从服务器端分批获得结果集。
MySQL默认是使用客户端游标的,因为通常情况下,程序处理的结果集不会特别大,对小结果集使用客户端游标效率更高:结果集一次性传输到客户端,客户端可以自行处理,服务器端也可以为其他客户端提供服务。但是针对大结果集,默认的客户端游标处理方式满足不了要求,针对这种情况,MySQL的Java客户端做了一个特殊的处理。下面参照官方帮助看一下。
云认证平台提供将认证记录导出为Excel的功能。但是当业务量慢慢发展起来以后,就碰到一个比较棘手的问题:需要导出的认证记录太多,导出Excel的时候经常碰到服务器内存溢出问题。例如当要导出30w条记录的时候,服务端内存占用会超过2G,继续增加Heap空间不是一个非常合适的方案,因为无法预知一共需要多少内存。
因此,这两天特意找了一下解决方案,终于有了解决方案。最终方案导出256w条记录的时候,服务器内战占用1.6G,不再继续增加。
Spring项目中使用数据库的方式多种多样,注入MyBatis、JDBC、Hibernate等等都是支持的。之前的系统是基于MyBatis的。
关于如何Spring Boot和MyBatis网上有很多文章,可以参考。有时间我也会专门写一篇介绍集成的过程。本文讲的就是MyBatis移植到Spring Boot方面碰到的一些问题,主要是事务控制方面的。
相比于Spring来说,在SpringBoot中使用Redis大幅简化了。通过使用spring-boot-starter-data-redis
,使用Redis变得非常简单。
基本步骤如下:
1<dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-data-redis</artifactId>
4</dependency>
1spring:
2 redis:
3 host: 192.168.2.230
4 port: 6379
5 password: xxxxxx
6 database: 1
7 timeout: 3000ms
1 @Autowired
2 StringRedisTemplate stringRedisTemplate;
就是这么简单。
原先云认证中有几个特殊加载的组件:
checkingServlet
:一个Servlet,用于检查系统的状态,主要用于负载均衡LB检测服务是否正常使用。该Servlet定义了了一组URL,可以用来检查系统的状态,例如:/checking/database(检查数据库是否可以访问),/checking/version(返回系统版本号),/checking(总体检查系统状态)等。这些功能理论上不适用Servlet也能够实现,但是由于历史原因采用了Servlet,这次一直也不考虑重新编写。ClientCertificateFromProxyFilter
:一个Filter,用于检查负载均衡转发过来的客户端证书,配合Spring Security完成双向SSL的认证工作。首先说一下背景信息。云认证平台是一个传统的Spring项目,从上到下可以分为:API层(对应Spring Controller)、Service层、DAO层(MyBatis)几层。为了方便处理,从开发规范上我们要求了Service层的异常在Service层自行处理。
为了实现这一点,在接口定义上,从API层调用Service层的请求会有统一的返回基类:CommonResult
。其定义如下:
1public class CommonResult implements Serializable {
2 private static final long serialVersionUID = 1L;
3
4 /**
5 * success 请求成功与否的标志。
6 */
7 protected boolean success = Boolean.FALSE;
8 /**
9 * resultCode 返回码。
10 */
11 protected String resultCode = "";
12 /**
13 * message 详细信息。
14 */
15 protected String message = "";
16 ...
17}
所有需要从API层调用到的Service层的返回值都是该类或者该类的子类。如果是Service层之间互相调用,则不受此限制。
本章的内容主要与AOP有关。