[DSL]模式匹配与请求跳转

具体上次撰写博客有一段时间了。这段时间内自己学些了很多,想了很多,尝试去理解了很多。结果之一是不管这个博客是为了什么而建立的,我还是把自己的技术思考记录下来。

本文描述是自己在工作中实现的一种DSL。DSL是个人今天年初左右才决定的发展方向,只是在具体工作中使用还是第一次。希望这可以作为自己在技术上选择性发展的一个好的开端。
由于是工作中开发出来的一种DSL,所以不会讲太具体的业务场景,而是以技术设计与实现为主。
本DSL的设计目的的核心如题,模式匹配和请求跳转。结合起来就是按照某种业务规则以模式匹配的方式决定请求是否跳转。更直接的一种表达方式是if else决定是否要redirect,只是这段if else是用DSL定义的。
使用DSL定义规则的好处在于更清晰地展现跳转的条件,避免技术实现给业务规则辨识带来的”噪音”,特别是复杂的跳转规则。其次是快速支持规则的变化,比如易变(不管是运行时易变,还是需求期易变)的规则,附带的另外一个好处是如果业务上需要动态定义的话,DSL可以快速支持,不再需要通过数据库设计,配置等传统方式。

Continue reading “[DSL]模式匹配与请求跳转”

[Java]运行时动态改变日志级别

其实这不是什么神奇的trick,只是很多人包括我在一直以来使用Java的日志框架比如log4j时没有意识到可以动态改变日志级别。如果你看到别人这么写了的话,很快就会转变观念的,我也是。

https://github.com/xnnyygn/dynamic-log-level 这个github是我整理的log4j动态改变日志级别的代码以及测试,有兴趣的可以参考下。

个人觉得这个trick可以用的地方还是程序员给自己开“后门”,特别是查线上问题时。由于很多人打印日志的级别的convention不一致。比如我喜欢非常详细的输入参数和输出结果用DEBUG,但是有些人就喜欢打INFO。一些复杂的逻辑有些人打了十几八行的INFO级别日志,看得我心烦,这帮人打INFO就像喝水一样……所以有些时候临时改变日志级别可能是有用的。不过从开发上来说,没有很好地统一日志级别规范,没有重视CR,是导致这个问题的主因,但是作为程序员要给自己留一手,比如动态改变日志级别之类的。

Continue reading “[Java]运行时动态改变日志级别”

[Java]简单的延迟初始化实现

本篇文章本来打算在上个月下半旬写的,不过个人状态一直都不太好,最近重新开始调整自己的技术心态,慢慢恢复,这篇也算是恢复中的一小步吧。

延迟初始化是我在上个月公司的一个项目中加入的一个技术,感觉小组对这种东西不感兴趣,所以还是放到自己的博客上来了。技术背景是业务对象是一个非常大的对象(胖领域模型),每次加载非常耗时,而且因为部分非关键服务的不稳定导致不需要这些服务数据的功能不可用。举个例子,导航页不需要用户的B信息,但是每次加载用户信息由于是在服务层(是的,传统的分层结构)加载的,无法直接控制加载的内容,所以导致在B服务失败之后,导航页不可用。

从技术角度来说,领域模型完全是保证业务执行的必要条件,但是实际情况并不是所有场景都需要这么严格的领域模型完整性的保证的,所以类似“降级”的处理方式应由而生。具体来说就是“按需加载”。实现“按需加载”的方式不仅仅只有延迟初始化一种,通过服务的参数显示地控制加载的内容也是一种方式,不过存在侵入性和严格的规约,对开发来说并不是那么方便。其实“按需加载”不是只在特定业务场景才有的需求,在ORM上,加载关联数据也是一种典型的“按需加载”。所以亦可以参考ORM在“按需加载”方面的实现。

在讲我的简化版延迟初始化之前,先大致讲下Hibernate这个ORM在按需加载上的处理。Hibernate主要支持one-to-one和one-to-many下的按需加载,Hibernate 3开始支持部分字段的按需加载(CLOB和BLOB不算在内,3之前就有支持)。实现上,部分字段的加载需要bytecode替换,one-to-many是通过改写集合的处理,one-to-one是通过代理对象,具体来说是CGLIB,因为JDK动态代理对象只支持接口。

了解这些对于我的实现有所帮助,不过除了one-to-many很快就能模仿写之外,部分字段的延迟初始化和one-to-one的处理都比较麻烦。one-to-one在查到结果为null时会比较麻烦,从调用上看应该是代理整个领域模型而不是单个延迟加载的对象,这块个人没有细究,因为我后来想想我不需要非常严格的模型字段规约,小小地修改模型理论上也不会有太大问题。而且部分字段的延迟初始化不是类似单个延迟初始化对象就可以解决的。

Continue reading “[Java]简单的延迟初始化实现”

用DLX解sudoku(数独)

最近在做scala99,其中有一道题目P97是sudoku(数独)。一开始我想到的是列举每个空格的可选数字,选择可选数字最少的的格子开始填写。每次填写,修改关联格子的可选数字。再次尝试填写。这里无解条件是存在没有可选数字的格子。还有就是easy或者normal级别的数独一般可选数字都是1个,分批填写很快就能完成。hard或以上就没有那么幸运了,必须猜测然后验证,存在回溯。

这里描述的算法其实就是搜索+启发。启发是选择性地从最少可选数的格子开始。猜测+验证,感觉这个问题属于NP问题。事实上也确实是,而且可以转化为NP完全的精确覆盖问题(Exact Cover),所以这个问题也是NP完全问题。

P97给的答案有两个,第一个就是搜索+启发,第二个是DLX。DLX是Exact Cover的高效解法。本文就是讲如何用DLX来解数独问题的。考虑到scala99上第二个实现大部分都是可变数据,所以我用Java实现了相应算法。

Continue reading “用DLX解sudoku(数独)”

jenkins小试

在看了某公司的质量管理之后,决定自己尝试下jenkins的持续集成功能。

参考资料是《Jenkins: The Definition Guide》,这本书第一章提到了持续集成的七个阶段,很有借鉴意义。

以下jenkins的安装配置和使用参照第二章。个人使用jenkins的版本为1.55,如有和书本或者和我操作不一致的请参照你的版本。
个人使用的测试工程是 https://github.com/xnnyygn/attic ,书本中提到的game of life下载太慢而且构建不成功,就换了个自己的工程。

Continue reading “jenkins小试”

NxN矩阵顺时针旋转90度

是一个面试题,我想了好久,果然好久不做类似算法题的话会思维迟钝。题目还有一个条件是每个点用4 bytes存储,实际上是每个点是int类型的暗示,这个矩阵就是int[][]的结构。

我是先考虑点的旋转规律的,用了一点三角函数和计算机图形学的坐标变换,实际上归纳法也是可以的,只是我没找出规律。之后是实际编码,比想象中要麻烦一些,和参考答案也是有点不同的。

首先说下变化规律,计算机坐标系下(x, y)点顺时针旋转90度后结果是(y, n – x),通过多次数据归纳也可以做,以下是图形学上的证明:

Continue reading “NxN矩阵顺时针旋转90度”

无聊之作:简单模拟电梯程序[Java]

记得刚开始学Java时用的是《Java大学教程》,这本书每章都在介绍一点电梯程序的样子,加上上班天天都要坐电梯,突发奇想想要做一个模拟电梯程序。

一开始没想清楚电梯程序的运行方式,但是知道几个关键表现。

  • 电梯有静止的时候
  • 电梯往往停在最后请求的楼层,也有最后静止在底层(1楼)的,这相当于电梯在没有请求时自己按了一下1
  • 电梯存在插队现象,即电梯在1楼,A在8楼先按,B在4楼后按,如果电梯没到4楼的话,后按的人先进

其实还有一个关键点,一般电梯都是有向上向下提示的,电梯上行时,A在8楼按下,B在4楼按下的话,B是不会插队的。不过如果把这个也考虑在内的话问题会复杂很多,因为操作电梯会有两步:指示方向和指出具体楼层。为了简化问题,我实际考虑每个楼层只有一个按钮,不指示方向,电梯内仍旧有具体到达楼层的按钮。这样的电梯可能有点怪,但也可以用。

Continue reading “无聊之作:简单模拟电梯程序[Java]”

看似简单的一个问题:请求速率限制问题

最近遇到一个场景,在每分钟错误计数达到250时发送消息。这里的每分钟并不是说整点的几分,有可能是现在16:16:16到16:17:16。
我了解到周围有人是用分钟的定时器来近似实现的,首先这样就限制了是整点的分秒,其次只限于对时间不敏感的场景,第三不能精确到秒,比如要求1次每秒的限制,因为定时器中任务执行很可能超过1s,而且还有并发的副作用。
那么直接在每次错误后向前扫描数量的笨方法呢?明显效率太低。

个人对于这个问题进一步分析,认为这个属于请求速率限制问题,并且找到的合适的关键字rate limit之后去google。发现stackoverflow有关于这类问题的讨论。在阅读了诸多资料之后,自己了解到两种专门针对请求速率限制的算法:Leaky Bucket和Token Bucket。下面简单介绍一下两种算法。
Continue reading “看似简单的一个问题:请求速率限制问题”

二叉查找树学习笔记

二叉查找树如其名是一种主要用于查找的每个非叶子节点最多有两个子节点(左右子节点)的树,其特点是:

任何a, b, c三个节点(父节点和两个子节点,左右子节点可能只有一个或者都没有)满足a < b < c。对于相等的值使用节点计数+1或者节点中的链表等不同的方式来处理。 a < b < c这个条件乍一看和二分搜索binary_search很像,实际查找元素时也是类似的,时间复杂度在O(logn)。 一般的二叉查找树(不考虑链表退化和平衡的处理)包含查找,创建树/增加节点,删除节点,前/中/后序遍历等操作,下面分别介绍。 Continue reading “二叉查找树学习笔记”