月度归档: 2018 年 9 月

  • 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 
  • 对象池技术

    对象池技术

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

    可能业务代码中用到的地方并不是很多,对象池的一个比较不错的应用点是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 
  • 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": 
  • 异常日志

    异常日志

    日志是应用运行中被动或者主动输出,记录了应用相关信息的工具。

    日志一般有多个日志级别,包括TRACE、DEBUG、WARN、INFO、ERROR、FATAL。

    其中TRACE,DEBUG一般生产环境不输出,INFO属于一般信息,用于记录应用相关参数,运行状态等,WARN级别属于轻微的警告,用于一些不应该出现的或者轻微的错误,不影响应用。而ERROR和FATAL就是我们所说的异常了,这种级别的日志用于记录会影响业务进行和程序运行的情况。

    合理的输出异常本身对于应用的维护性有着很大的帮助。

    异常的类别

    要输出合理的异常信息,首先要明确有哪些异常,在我看来,异常无非有三种

    程序异常

    程序异常就是我们所说的真的挂了,也就是编程语言或者其运行环境自身提示异常,比如空指针异常。这种异常发生应用就已经出错的。这种错误不应该是开发人员手动输出的,而一般由公共的Exception Handler处理。这种异常理论上不应该直接输出,而是应该通过参数检查来避免,当检查不通过时应该转为输出可读的异常信息

    调用下游依赖异常

    现在巨型应用慢慢开始拆分,就出现了上下游调用的情况。比如一个财务系统,可能涉及到一些人员的信息展示,而这些信息可能由另外一个专门的人员服务来负责。那么当调用下游时就有可能出错,可能是返回不正确(null)也可能是超时,特别是互联网公司,很多超时都是200毫秒,网络抖动就会报错。

    业务报错

    对于任何输入都应该有验证,如果传入了错误的参数或者缺乏必要的参数,这种是逻辑上需要预处理的,而且技术上这种异常不是来自正常请求,因为调用方应该提供正确参数,若有问题应该在测试阶段暴露。

    还有一种报错来自应用框架。现代应用开发很少从底层弄上来,一般会使用一些框架,比如Spring Franework。这种框架自身会输出两种日志,一种是启动日志,一种是异常日志。

    比如Spring MVC在http请求缺乏必要参数时会抛出异常,这种异常大部分应该被开发人员手动转为有意义的异常。当然这个要看情况。

    异常日志的意义

    异常本身就代表了应用没有正常运行了,而异常日志的意义我个人觉得有几类:

    需要代码修复

    这种主要针对第一种异常,代码真的报错了,不管是什么原因,都代表这种情况可能出现,那么这里就需要人员去处理,处理之后重新上线。

    如果测试是充分的,那么这种异常一般是异常的调用或者异常的数据产生(异常不代表数据是错的,这里主要强调非正常数据,数量不大),那么这种问题尽快修复就行了。

    需要沟通

    这种主要是针对第二种异常,下游依赖出现问题的时候,那么首先要通知下游的服务提供方,它们可能有自己的监控,也可能没有,也可能这种异常本身没法被提供方的监控所感知,不管怎样,首先要让相关人员知晓情况,然后再说评估影响,作出数据修复或者继续观察。

    需要知晓

    有些异常可能只是让人心里有数,比如不正确的外部参数,这种如果自己的应用没有问题,那么一般都是不正常的请求来源,不需要提供正确的响应,不如说这种情况下的异常才是正确行为。

    但是这种情况都需要相关人员知晓,如果异常进一步扩大或者极速增加,那么还是需要采取一些防御手段的。

    合理的异常日志内容

    异常日志的内容要有合理有意义,主要是能够良好支撑我们对于日志的期望。日志框架的选择和收集方式我们这里不谈,主要说说日志的内容。

    互联网公司提供产品和服务给用户,通常这种服务是全天候的,根据业务的情况还可能出现非工作日业务量更大的情况,那么就需要有团队成员值班。而一个人应该了解自己团队所负责的服务和业务,但是对于每一个模块,每一行代码不可能都非常熟悉,那么异常日志就是提供一个快速判断,进而作出正确决策的输入来源。

    首先完整的堆栈信息是必要的,特别是对于程序异常,光跑出一个NullException但是没有具体的代码行数是没有意义的,完整的堆栈可以快速定位,这种情况的保证一般是框架自带的,偶尔也有手动输出的,只要不忘记传入exception就行了。

    logger.error("计算配送单{}时长失败", id, e);

    如果错误是框架统一异常处理的,一般会输出堆栈信息,比如Spring的DefaultErrorWebExceptionHandler

    对于下游调用异常的情况,首先为了方便沟通,需要输出的是调用方的信息,比如

    logger.error("调用人员服务超时,当前订单{}", id,