博客

  • 利用H2的自定义函数更好的支持测试

    在写DAO层的单元测试时,用mock的写法意义不大,因为DAO层的测试更多的关注是Java对象的传递和生成SQL的正确性,所以需要链接真实的数据库。

    但是对于单元测试,真实的数据库是很重的,还要依赖于数据库本身的初始化和数据情况。H2作为一个基于Java开发的嵌入式数据库,支持在内存运行,也支持文件模式,非常适合用在单元测试。因为Spring Boot等框架对于这一块的支持很全面了,特别是Spring Boot默认的JDBC测试如果没有配置,会自动启动一个H2的内存数据库。

    H2虽然支持很多模式,可以尽可能的模拟不同的数据库,但是还是有一些支持是不完整的,一些数据库特有的方法H2是不支持的,比如MySQL的UNIX_TIMESTAMP。特别是业务上基本都有创建时间和更新时间这两列,unix_timestamp函数大量使用。

    这里可以使用H2的一个特性–用户自定义函数,支持两种使用方式

    1. Java方法预编译
    2. Java源代码直接使用

    这里来看看如何让H2支持unix_timestamp方法:
    首先新建一个新Java类

    public class H2Extended {
        public static int unix_timestamp() {
            return (int)(System.currentTimeMillis() / 1000);
        }
    }

    然后使用SQL创建一个别名,并将别名链接到我们的Java类的对应方法中

    CREATE ALIAS UNIX_TIMESTAMP FOR "com.huangyunkun.tool.H2Extended.unix_timestamp";

    然后在初始化Datasource的时候执行该SQL语句就行了

    参考

    http://www.h2database.com/html/features.html#user_defined_functions
    https://hyrepo.com/tech/h2-unit-test/

  • 自动化发布sited插件

    自动化发布sited插件

    最近又在弄多多猫的sited插件了,写插件本身没啥了,写好本地build一下,然后发布到插件中心。

    这里我注意到一个版本问题,大多数情况需要自己手动改一下版本,最近在用阿里的云效CodePipeline,想到这里可以自动化一下。

    阿里云的CodePipeline其实就是jenkins,从环境变量中可以获取当前Pipeline号,可以把这个作为版本号。然后上传的化,其实官方的上传主要就是一个普通的上传,一个curl命令就可以搞定。

    echo "Publish to sited plugins site"
    head dist/index.sited.xml
    curl -v 'http://sited.noear.org/dev/dev.ashx' -F "file=@dist/index.sited.xml"

     

  • 快速使用SQLAdvisor

    快速使用SQLAdvisor

    美团点评开源的SQLAdvisor可以帮助分析SQL语句,同时提供优化建议。

    遗憾的是官方没有提供快速的使用方法,需要自己安装依赖并编译,使用起来确实不是很方便。今天做了一个Dockerfile,直接把工具打到Docker中,这样就可以快速使用了。

    使用如下:

    docker pull htynkn/sqladvisor

    验证,输出帮助信息

    docker run -rm htynkn/sqladvisor --help

  • Java诊断工具Arthas

    JVM自身提供了大量的诊断和调优工具,从jps、jstack、jmap、jhat、jstat、hprof等到visualvm等,提供了JVM进程信息,堆栈,堆内存等内容。

    Arthas是阿里开源的Java诊断工具,整合很多功能,而且是交互式。部分功能使用字节码增强技术,能够获取更多信息,而且还支持长时间后台监控,确实是一个不错的工具。

    Arthas安装比较简单,直接运行官方的sh脚本就行了,然后运行启动。这里要求本机必须有telnet,但是Mac默认已经移除了telnet,所以需要自己安装一下brew install telnet

    来几个使用示例

    查看类加载

    查看反编译代码

    这有一片文章比较详细的展示了Arthas的使用:

    http://hengyunabc.github.io/spring-boot-arthas-NoSuchMethodError/

  • 说图

    说图

    什么图

    图是绘画表现出来的形象,图对比别的表现方式更生动,更直观。

    当然作为程序员常接触到的无非就是流程图,ER图,对象图,架构图等等。

    为什么

    工作中需要这些无非就两个用途,自己看;给别人看。

    我个人观点是图可以不绚丽,但是要清晰,可读,并且符合通用的图标标准。

    比如一个时序图,每个参与者是actor还是普通参与者,还是数据库等等,不同的图标代表不同的意义,可以用的比较简单,但是不能误用。

    工具

    画图工具比较多了,主要分两个大类,一是可视化绘制,即所见即所得那种,结果直接可控,而且细节调整度很大,但是操作比较繁琐,容易陷入细节,且工具比较多,付费的或者在线的比较多。

    Visio绝对是这种类型的一个佼佼者,唯一的缺点是平台限制。Mac世界可以用OmniGraffle,当然也是挺贵的。

    Linux世界可以用开源的Dia,我个人绝对还是不错的。

    在线版本的推荐Processon,使用免费,唯一的担心就是需要联网,其次就是有些是公司内部的东西,总不是那么放心。

    当然还有大神使用Excel或者Powerpoint也可以画出很牛的图,不过我觉得那个对于功底要求有点高了。

    另外一种是代码生成图像的,比较符合程序员的风格,可以直接通过代码生成最后的图,操作比较直接,缺陷是反馈比较慢,可调整型也不好。

    我曾经尝试过D3.js,这个控制层次很低,效果可好可坏,看个人了。

    我比较推荐PlantUML,这个画图比较直接,效果还过得去,亮点是对于程序员需要的相关图支持很好,比如UML,时序图,类图,组件图,部署图等等。

    具体的话有本地版本,可以转化为图片,也有在线免费版本,直接把代码转为图。

    比如下图:

    Bob-and-Alice
  • Git多套配置的条件应用

    Git是有配置的,比如用户名和邮箱,在一些情况下对于不同的项目可能需要不同的Git配置,比如在家需要做一些公司相关的事情,那么gitconfig的邮箱就是公司邮箱,但是平时有一些自己的代码,放Github那种可能又需要配置其他信息。有时候ssh key也不一样,总的来说需要一种多环境配置的支持,之间相互隔离。

    一种办法是将全局配置和单一配置分开,即单个项目的git目录中加一个config,这样的配置主要问题是每一个需要单一配置的项目都需要这么做。

    另外一个办法是使用条件应用,这种配置可以根据目录来区分,比如有一个目录叫work,那么work中就使用专门一套config。

    [user]
    name = hyunkun
    email = [email protected]
    
    [includeIf "gitdir:~/OpenSource/"]
    path = ~/OpenSource/.gitconfig

     

  • Hello Flutter

    之前用过React Native,同时开始Android端和iOS端还是比较舒适。

    最近Flutter很火,仔细看了一下,感觉比RN要快一些,所以就上手看看。

    首先上遇到的问题就是Flutter的安装,因为我是Mac,直接写了一个HomeBrew的脚本 https://github.com/htynkn/homebrew-formulas/blob/master/flutter.rb

    另外一个问题就是依赖构建,我平时都使用的maven aliyun的镜像,但是Flutter创建出来的项目很奇怪,访问jcenter会报错,即便我的网络问题解决了。只有手动指向aliyun的地址,但是修改了build.gradle文件以后还是出现了jcenter报错。

    最后是在

    $flutterRoot/packages/flutter_tools/gradle/flutter.gradle

    中找到了jcenter的配置,手动修改以后就好了。

    自动化也是一个很重要的,Travis的配置相对比较繁琐,需要Java,Android,Dart和Flutter环境。

    Travis提供了Android支持,同时可以指定对应的构建工具版本。所以手动安装Flutter就行了。

    language: android
    os:
      - linux
    
    android:
      components:
      - tools
      - platform-tools
      - build-tools-27.0.3
      - android-27
      - extra-android-support
      - extra-google-google_play_services
      - extra-android-m2repository
      - extra-google-m2repository
    
    before_install:
      - git clone -b beta --depth=1 https://github.com/flutter/flutter.git
    
    install:
      - export PATH=./flutter/bin:$PATH
    
    script:
      - flutter build aot
      - flutter build apk
    
  • 对象池技术

    对象池技术

    当对象的创建比较昂贵的时候,且生命周期通常较短的时候,将对象池化是一个比较不错的选择。对象池给对象提供了一个缓存。

    可能业务代码中用到的地方并不是很多,对象池的一个比较不错的应用点是RPC框架中的序列化部分,很多序列化框架自带了对象池,比如KryoPool。
    改造现有代码使用对象池并不复杂,无非就是选择一个对象池框架,然后修改对象的获取和释放。

    比较常见的对象池库肯定是apache-common-pool了,它提供了数种不同类型和实现的池,大部分时候直接用它就行了。

    一个简单的例子

    public class ReaderUtil {
        
        private ObjectPool<StringBuffer> pool;
        
        public ReaderUtil(ObjectPool<StringBuffer> pool) {
            this.pool = pool;
        }
    
        /**
         * Dumps the contents of the {@link Reader} to a String, closing the {@link Reader} when done.
         */
        public String readToString(Reader in)
            throws IOException {
            StringBuffer buf = null;
            try {
                buf = pool.borrowObject();
                for (int c = in.read(); c != -1; c = in.read()) {
                    buf.append((char) c);
                }
                return buf.toString();
            } catch (IOException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeException("Unable to borrow buffer from pool" + e.toString());
            } finally {
                try {
                    in.close();
                } catch (Exception e) {
                    // ignored
                }
                try {
                    if (null != buf) {
                        pool.returnObject(buf);
                    }
                } catch (Exception e) {
                    // ignored
                }
            }
        }
    }

    可以看出使用对象池的关键就是将对象重置这个行为交给对象池处理。池的使用可能有多线程竞争问题,不过大部分库都能够处理这个问题。

    当然一般考虑到对象池了自然是有性能上的需求,这里有一些小众的对象池

    https://github.com/DanielYWoo/fast-object-pool

    https://github.com/chrisvest/stormpot/

    https://github.com/vibur/vibur-object-pool

    还有一些是其他项目中的对象池,比如使用数据库连接池中的对象池(https://github.com/brettwooldridge/HikariCP)

    参考

    https://commons.apache.org/proper/commons-pool/
    https://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool2/impl/GenericObjectPool.html

  • zalando自动化测试

    之前的项目大量使用了selenium自动化测试,当时使用了selenium grid,也是基于Docker的,但是问题挺多的。主要有两个方面

    1. 管理比较弱
    2. 缺乏统一的日志管理和其他信息收集

    节点的数量完全靠自己启动和管理,需要就启动一个,觉得多了就删除一个。这个问题还好,无非就是资源浪费或者资源缺乏的问题,手动一下就行了。

    第二个问题就比较复杂了,selenium测试毕竟不是完全真实的,总会遇到一些不稳定的case或者自动化运行没法通过,但是手动可以的情况。之前的操作基本是在可疑位置截图,然后去镜像上把图片下载下来研究。

    今天看到公司内部有人调研过相关的设施,提到了zalando,它是一个Selenium Grid的扩展,基于Docker可以任意扩展节点(支持Firefox和Chrome),自带一个Dashboard,更关键的是内置了VNC支持,可以直接实时查看屏幕,还支持录屏幕!!!

    看下面的图示来感受一下

  • k8s亲和性调度

    k8s

    Kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署,调度和节点集群间扩展。

    k8s并不单单是Docker的管理平台,它还支持其他容器技术,只是更多的我们使用的是Docker而已。

    k8s的调度能力是一大特色,让人不需要关注Pod中具体的部署、调度细节等等,但是有时候我们会有一些特别的需求,需要调度能够满足。

    调度需求

    列举一些常见的调度需求:

    1. 希望能够在不同的机房部署(异地)
    2. 对于服务X有很强依赖,希望能够部署到同一个网络中(减少网络开销)
    3. 应用对于某种资源要求很高,尽量部署在特定的机器上(比如GPU资源消耗大)

    k8s默认的调度规则如果不能满足,就需要使用别的特性来满足了,这里是k8s自带的Affinity配置。

    例子

    来看一下强依赖的例子,希望调度能够满足部署到一起

    假设强依赖的服务是service-X,那么这种关系是Pod和Pod的,使用podAffinity

    affinity:
        podAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - service-X

    如果是我们提到的希望部署到特定的节点上的,可以使用nodeAffinity

    {
      "nodeAffinity": {
        "requiredDuringSchedulingIgnoredDuringExecution": {
          "nodeSelectorTerms": [
            {
              "matchExpressions": [
                {
                  "key": "kubernetes.io/hostname",
                  "operator": "In",
                  "values": [
                    "gpu-node"
                  ]
                }
              ]
            }
          ]
        }
      }
    }

    参考

    https://www.bladewan.com/2018/08/12/kubernetes_affinty/