状态机简介
之前通过《状态模式》介绍了一下状态模式的使用,这次我们来介绍一下有限状态机 维基百科中是这样定义的:有限状态机(FSM)又称有限状态自动机(FSA),简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型,在任何给定时间都可以恰好处于有限数量的状态之一 其实状态模式也可以算是状态机的一种实现,除了状态模式,还有一种实现方式就是创建一个描绘迁移的数据表,该表被一个处理事件的处理引擎解释。引擎查找与事件匹配的迁移,调用响应的动作并更改状态,这样状态机的逻辑全部集中在了一个地方 所以表驱动的状态机和状态模式的主要区别就是:状态模式对于状态相关的行为进行建模,而表驱动的方法着重于定义状态转换 这里我们介绍一下表驱动的状态机实现,虽然这方式叫基于表驱动的状态机,但是这个表不是说特指我们平时用的数据库的表,而是一种关系的记录 现在已经有了很多成熟开源的状态机可以使用,如 Spring Statemachine、Squirrel Statemachine和stateless4j,平时工作如果需要,直接使用就好而不需要自己再开发,这次就以最轻量级的 stateless4...
单元测试之Mock
单元测试是对我们写的代码的最小单元进行测试,一般来说就是对函数(方法)进行测试,大家都知道写单元测试的好处,但是具体要怎么写呢?被测试的类可能依赖外部的类或服务,这个依赖的外部接口如何Mock,依赖注入的类如何替换成自己Mock的类等等,下面就介绍一种常用的Mock方式 首先,既然是单元测试,那么要做的第一件事就是屏蔽掉对外部的依赖,不让它们对我们要测试的方法产生影响。屏蔽的方式一般就是通过Mock,将调用的真实方法替换为我们设置好的mock类,这里简单介绍一种Mock框架 Mockito 首先需要引入对应的依赖,如果使用Maven则需要在pom.xml中添加如下内容 123456<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-all</artifactId> <version>1.10.19</version> <!-- 可以替换最新的版本号 --> <scope>t...
Flyway简介
平时我们写代码一般使用Git等工具来进行版本管理,而Flyway可以理解为是用来进行数据库的版本管理的,它的功能是比较强大的,可以帮忙我们自动执行SQL,确保各个环境(开发、测试等)数据表的同步等,详细内容可以参考文档,这里简单介绍一下常用的使用方法 基本maven使用 引入依赖 12345<dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> <version>7.0.0</version></dependency> 创建SQL脚本并执行 在src/resources下创建 db/migration目录,在其中创建对应的SQL文件,其中又分为不同的文件类型,基于约定由于配置的原则,通过文件命名方式进行区分 版本迁移以V开头,只会执行一次;回退迁移以U开头,一般不使用;可重复执行迁移以R开头,每次修改后都会重新执行 版本迁移类型 在db/migration目...
Guava EventBus
在我们日常开发中经常有这种类型的场景: 新建用户后,需要进行一些操作,如发送优惠券等(和创建用户本身无关的操作) 数据变更时,对应的展示表格等信息需要进行对应的更新 即当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新 观察者模式(发布-订阅)就是这种情况下的一种解决方案,使用这种方式可以让解耦发布者和订阅者,互相不需要知道对方,之前一篇文档中简单介绍过Spring中的事件使用,这次介绍一种非Spring环境下Guava提供的EventBus的使用 使用 下面简单介绍一下EventBus的使用,使用起来其实是比较简单的,代码如下 创建事件类 1234// 一个简单的普通类public class UserCreateEvent { // 定义需要的属性} 定义监听者 12345678910111213141516171819// 定义一个发送短信通知的服务public class SendSmsService { // 使用 @Subscribe 标识此方法用来处理事件消息 @Subscribe // 方法...
记一次调用HTTPS接口证书问题排查
问题 发现系统调用HTTPS接口时出现异常,异常信息如下 123javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:unable to find valid certification path to requested target 排查 怀疑1: 接口提供方的证书过期了?(其实仔细看日志的话就可以排除这个可能) 操作: 通过本地浏览器打开对应url,浏览器显示正常的安全信息,证书也未过期,oracle的证书样例如下(例子中的根证书颁发机构为 DigiCert Global Root CA ),这时可以排除证书问题 **怀疑2: ** 接口换了证书,服务器没有对应的根证书? 操作: 通过浏览器访问,查询到接口提供方对应的根证书颁发机构为:DST Root CA X3 上网查询到 获取服务器(lin...
Spring事务基本原理
本文主要对基于XML和基于注解配置声明式事务的执行过程进行介绍,因为Spring的事务是基于AOP的,所以最好了解一些AOP相关的基础内容,下面我们就开始进入正文 配置使用 我们使用一个功能,首先就是需要配置,这里我们写一个基于Mybatis的配置,dataSource相关的配置都忽略,直接看事务相关的配置 123456<!-- 声明使用的事务管理器 --><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <constructor-arg ref="dataSource" /></bean><!-- 启用注解式声明事务 --><tx:annotation-driven transaction-manager="transactionManager" /> 这样配置后,在...
从源码看ThreadLocal
对于多线程并发对数据的修改的情况,其实除了使用锁或者CAS机制之外,有的情况我们完全可以为每一个线程分配单独的数据,这个数量只能在对应的线程下才能访问到,这样就能避免资源的争抢 或者对于单次请求全局的一些信息,比如当前请求对应的用户信息,可以不通过参数的方式依次传递,而是在全局的一个地方维护,比如当请求进来时,就将当前用户的信息存储进去,但是因为我们的服务是多线程的,同时可能有很多的请求,所以需要用户信息有线程隔离的能力,不能访问到或覆盖了别的线程的用户信息 JDK提供的对应功能的类就是ThreadLocal 使用 先来看下最简单的例子 12345678910111213141516171819202122private static ThreadLocal<String> nameThreadLocal = new ThreadLocal<>();private static ExecutorService executorService = Executors.newSingleThreadExecutor();public static void ...
Redis RDB与AOF
Redis的存储是基于内存的,这就有意味着,如果服务重启那么所有的数据都会丢失,这是我们不能接受的,为此,Redis提供了 RDB 与 AOF 两种持久化机制 简介 RDB: 对于RDB,我们可以把它理解一个定时的快照,就是每隔一段时间(或其他策略),它会创建一个当时所有数据的一个快照,默认存储到的文件为 dump.rdb AOF: 因为RDB创建的是所有数据的快照,这点就决定了它不可能进行高频率的执行,但是如果不经常性的执行,那么当出现如服务器宕机等情况时,会丢失从上次快照到当前的所有操作数据。为了解决这个问题,AOF应运而生,它会在每次执行操作的时候,将这个操作命令进行持久化存储,默认存储到的文件为 appendonly.aof,这样恢复的时候,可以将 aof文件中的所有命令重新执行一遍,即可恢复数据 配置及使用 RDB和AOF的配置都在redis的配置文件redis.conf中进行配置,我们看下主要的配置 RDB的主要配置如下 1234567rdbcompression yes # 是否使用 LZF算法 压缩字符串dbfilename dump.rdb # 持久...
Netty服务端使用及简介
最近因为工作原因,学习了一下Netty,这里写个笔记记录一下 Netty是什么呢?它是一款异步的事件驱动的网络应用程序框架,使用它可以快速的开发出可维护的高性能的面向协议的服务器和客户端—《Netty实战》 这是有两个关键词是异步和事件驱动,先来简单介绍一下 异步是指什么呢?就是发送消息和接收消息是异步的,如调用发送方法后,进行的异步发送,你不知道到底发送出去了没有,如果想要知道结果怎么办呢?这时候可以设置回调函数,发送结果会通过回调函数传回来,当然,其实也可以阻塞同步等待结果,不过那可能就会影响其他数据的处理了 事件驱动呢,我理解就是接收数据等的时候,如在数据可以到达可读取的时候,IO线程会通知调用我们相应的方法进行数据处理流程 好了,下面我们看一段来自Netty官网的服务端代码 12345678910111213141516171819202122232425262728293031323334353637public class DiscardServer { private int port; public DiscardServer(int po...
C语言实现的封装,继承,多态
提到面向对象编程,我们想到的就是封装、继承、多态,但是其实这几个特性并不是只有面向对象语言才能实现,面向过程的C语言也是可以支持实现这三个特性的,下面我们来具体看下 封装 1234// point.hstruct Point;struct Point* makePoint(double x, double y);double distance(struct Point *p1, struct Point *p2); 123456789101112131415161718192021// point.c#include "point.h"#include <stdlib.h>#include <math.h>struct Point { double x, y;};struct Point* makePoint(double x, double y) { struct Point* p = malloc(sizeof (struct Point)); p->x = x; p->y = y;...
