博客

  • 拉取Github PR代码到本地分支

    有时候在Github上协作,比较常见的例子就是你fork了一个项目,这个时候有人提交了一个PR到源项目,而然后你希望研究一下PR。如果你fork以后的项目(你有完整权限的那个,用户是你自己的)名字为origin,那么首先做的就是添加upstream指向源项目。

    这里以Dubbo的一个PR为例,https://github.com/apache/incubator-dubbo/pull/1941

    这里可以看到这个PR的ID是1941。首先检查upstream指向。

     

    然后fetch PR,命令如下:

    git fetch origin pull/1941/head:repack-org-apache
    git checkout repack-org-apache
    
  • 利用docker快速启动一个hive

    临时要用一下hive,本机是没有装的,又想要一个最小集群,快速起起来的,找了一会儿发现一个好的库https://github.com/big-data-europe/.

    这个库下面从Spark到Hadoop,从hbase到hive,应有尽有.以hive为例,https://github.com/big-data-europe/docker-hive

    下载整个库以后直接docker-compose up.默认端口是10000,直接使用就行了.…

  • 使用Gradle构建缓存服务加速构建

    使用Gradle构建缓存服务加速构建

    有时候会在本地使用docker-compose,如果compose中没有image文件这些还好,如果有那就要设计到构建了,因为构建的时候都是在镜像中,所以相对的速度比较慢,没法重复构建缓存。有时候即便不在docker环境中还是想重用缓存来加速。

    Gradle官方就提供了一个这样的服务, build-cache-node,这个服务本来是为了企业版服务的,但是非企业版也可以部分使用。因为提供了docker镜像,直接启动就行了。

    docker run gradle/build-cache-node

    在项目配置中需要添加Cache Server地址,比如

    buildCache {
        remote(HttpBuildCache) {
            url = 'http://gradle-cache-server/'
            push = true
        }
    }

    然后在构建中加上–build-cache就行了。

    如果需要认证也可以自己修改配置。

    参考:

    https://hub.docker.com/r/gradle/build-cache-node/…

  • 基于Zookeeper的分布式锁

    ZooKeeper是Apache软件基金会的一个软件项目,它为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。 ZooKeeper曾经是Hadoop的一个子项目,但现在是一个独立的顶级项目。

    在Zookeeper中,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据。节点的类型又分为临时节点和永久节点,又可以带有序号。锁的本质是资源的占有,那么就需要有一个对象,在ZooKeeper中就可以是它的znode。所以最直接的锁思路就是占有一个znode。步骤如下:

    1. lock方法调用时尝试创建一个临时节点
    2. 如果节点创建成功,那么即获得锁
    3. 创建节点失败的就需要等待(等待可以是固定等待时间或者是使用Watch机制)
    4. 获得锁的线程释放锁时断开和ZooKeeper的链接即可

    这个方法的问题在于等待,如果采用Watch机制,那么就容易出现大量订阅。

    更常见的一种方法是将操作集中在一个目录中,比如/locker/user/。

    在该目录下创建临时有序节点,那么没有获得锁的线程其实是可以感知其他线程的,比如最小序列是000001,而当前线程的序号是000010。大致方法如下:

    1. lock方法调用时创建临时有序节点
    2. 判断当前目录下的最小节点是否是自己持有的,如果持有就获得锁
    3. 如果最小节点不是自己获得的,那么就需要等待,可以watch最小节点的删除事件,而不是目录的情况
    4. 释放锁时断开链接即可

    当然原理比较容易理解,但是实际使用中可以直接用封装好的,比如curator-recipes中的InterProcessMutex,InterProcessReadWriteLock等。

    参考资料:

    https://zookeeper.apache.org/doc/r3.1.2/recipes.html

    https://www.jianshu.com/p/5d12a01018e1

     

     …

  • 分布式环境下事务一致性

    在当今流行微服务体系结构中,一个看似简单的功能可能需要调用多个服务,并操作多个数据库。比如创建用户操作,首先需要在服务A创建用户的帐号,在服务B中创建对应的积分记录,然后服务C中添加用户优惠信息等,由于网络,服务自身的可用性等,这几个操作中的一个或者多个可能会失败,为了保持事物一致性,就必须有对应的手段或者技术。

    实现分布式环境下的事务一致性保证有以下几种方式:

    1. 两阶段提交协议
    2. 三阶段提交协议
    3. TCC 协议
    4. 基于可靠消息投递方案
    5. 基于补偿模式方案

    两阶段提交包括准备阶段和提交阶段,主要需要事务协调者TM和资源管理器RM参与。首先在准备阶段,首先 TM 发送指令到 RM,RM 对自身状态进行充分评估,如果 RM 经过评估认为操作能够完成, 那么锁定资源但是不提交。在提交阶段,如果每个RM都反馈成功准备的信息, 再进入第二步执行确认操作,即资源预留及操作均成功,TM发送RM提交操作的指令, RM如果没有成功,则需要执行逆操作来取消第一步操作的影响。

    两阶段提交协议的主要问题在于虽然一致性很强,但是锁太重,如果TM出现故障,会出现阻塞。

    三阶段提交协议从字面看就是对两阶段的补充,引入了超时机制,将准备阶段拆分为询问和预提交两个部分。询问时RM只响应是或者否,不锁定资源,超时就直接终止。预提交的时候RM再记录日志和执行锁。

    很明显,三阶段虽然多了一个步骤,但是实际上只是提交发现无法执行的情况,之后并没有特别的改进。

    TCC 协议会将一个任务拆分成三个环节:尝试(Try ) 、 确认(Confirm ) 、 取消(Cancel )。正常的流程是第一步先执行尝试操作,如果不成功,就执行逆操作取消第一步的影响。它和两阶段的区别在于RM需要具备一定的自动修复能力,如果RM中任何一个失败,就要求所有RM执行第一步的逆操作。Restful情况下的设计有一篇论文比较详细《Atomic Distributed Transactions: a RESTful Design》,公开下载的可以看看。

    基于可靠消息投递方案相对比较简单,对于可以异步处理的事务,采用消息队列来处理,只要保证了幂等性(确保消息任意多次执行所产生的影响均与一次执行的影响相同)那么就可以重复重试即可。

    基于补偿模式方案对应更业务一些。在目前所知的情况下,一旦有操作出现了不正常的情况,我们需要及时的去修复系统中有问题的子操作。这些操作最终的目的都是让系统的各个部分达到最终的一致性。

    这里网上找到了可靠消息投递方案和TCC的示意图,可以参考一下:

    参考链接:

    https://segmentfault.com/q/1010000000304596…

  • 阿里云大学认证

    之前阿里云搞活动,顺手买了阿里巴巴编码规范(Java)认证。https://edu.aliyun.com/course/417

    今天下午仔细看了一遍,其实内容就是之前免费下载的PDF,而且版本还不是最新的,认证学习里面的版本是1.3.0,点击下载过去下的是1.3.1。

    认证的时候需要开启摄像头,验证身份证,感觉还蛮正式的,唯一体验不好的就是摄像头会定时抓拍,经常头动一下,或者摄像头位置没对,就会弹窗警告。。。

    最后的证书看着还比较正式,还有过期时间。总的来说我觉得如果自己能认真看资料,那下载PDF就行了,如果需要点动力,可以考虑花钱买认证。

  • 根据Commit ID自动触发Docker Hub的构建

    根据Commit ID自动触发Docker Hub的构建

    有时候自己的Docker镜像需要上传到Docker Hub上,为了保证镜像的更新,我们需要出发Docker Hub的构建。具体操作有以下几种:

    1. 从Travis CI(或者其他CI)来触发,每次提交自动构建新的镜像到Docker Hub
    2. 从其他镜像触发,比如你的镜像依赖了tomcat,那么就可以在Docker Hub中配置tomcat镜像一更新,你自己就更新
    3. 通过Docker Hub手动触发

    这三个方法看起来第一个最好,但是有个先决条件,项目库或者说CI权限在自己手动。如果你的镜像编译了第三方的repo,那其实你没有办法配置webhook这一类的通知来触发构建。

    因为最近在用阿里云函数计算,所以想到一个取巧的方法,通过commit id来判断是否为最新构建,大致思路如下:

    1. 在Dockerfile中输出commit id
    2. 通过Github API获取目标项目的最新commit id
    3. 通过Docker Hub API获取最新构建的日志
    4. 对比日志,如果commit id一样,就说明构建是最新的
    5. 如果不一样,就调用Docker Hub API触发新的构建

    在Dockerfile中添加:

    RUN git rev-parse HEAD

    然后构建日志中就有commit id信息了

    触发构建用的Docker Hub API地址可以在Build Settings中找到

  • 使用阿里云函数计算自动同步fork repo

    使用阿里云函数计算自动同步fork repo

    在参与一些开源项目的时候,因为工作流的原因,经常需要从upstream的repo同步改动到自己fork的repo中。一般来说可以使用git命令来完成,大致步骤如下:

    1. git remote add upstream …
    2. git fetch upstream
    3. git pull master
    4. git rebase upstream/master
    5. git push

    操作到算不上繁琐,我是本地写了一个shell,自己手动运行。

    但是有时候工作在多个repo上,可能会忘记更新,特别是有一些项目活跃度很高,要是一两天没有同步,开工的pull request可能就要花大量时间去解决冲突。对于团队项目,那还需要大家轮流负责同步的事情,虽然网上有很多服务可以做到自动同步,但是一般都要求上游项目仓库接入,如果自己只是项目的贡献者,还是比较难去操作的。

    最近发现Github的API支持Patch方法,可以做到一个api调用就完成上面的几个git命令,同时流量消耗很小(发送一个json body就行了),这样就可以放到阿里云函数计算上,通过定时触发来自动化这个问题。

    为了简单起见,还是使用nodejs 8的环境,依赖库使用async和request。

    我们以dubbo为例,首先需要获取上游项目的最新commit sha。调用地址:https://api.github.com/repos/apache/incubator-dubbo/branches/master

    然后下一步发送一个PATCH请求到自己fork的库中,以我自己的repo为例,请求地址:

    https://api.github.com/repos/htynkn/dubbo/git/refs/heads/master

    发送的body中包含两个字段,一个是上一步获得的sha,一个是是否强制更新的标识符。

    {
     "sha": sha,
     "force": false
    }

    步骤就这两步,其次就是需要一个token,可以自己在Github配置汇总生成,只需要repo权限就行了。

    完整代码如下:

    var request = 
  • Docker多步构建生成dubbo-admin镜像

    Docker多步构建生成dubbo-admin镜像

    Docker是支持多步构建的,对于需要编译源代码的那种构建,多步构建一方面可以获得更小的镜像,另外一方面也不需要手动清理源代码和别的文件了。

    有时候本地调试的时候需要起一个dubbo-admin看一下,但是每次都是启动一个tomcat,然后拷贝war,久了也有点麻烦。想从Docker镜像启动一个,但是官方没有提供。网上有不少个人构建的镜像,但是版本有些旧了。索性自己搞一下放在Docker hub。

    手动操作比较直接,克隆代码库,然后maven打包出war包,放tomcat运行即可。多步构建的Dockerfile如下:

    FROM maven:3-jdk-8
    RUN git clone --depth 1 https://github.com/apache/incubator-dubbo-ops.git /source
    WORKDIR /source
    RUN mvn package -f dubbo-admin
    
    
    FROM tomcat:8.0-jre8
    RUN rm -rf /usr/local/tomcat/webapps/
    COPY --from=0 /source/dubbo-admin/target/*.war /usr/local/tomcat/webapps/ROOT.war
    EXPOSE 8080

    运行的时候提供一下注册中心的环境变量(dubbo.registry.address)即可。

    如果是docker-compose,简单的例子写法如下:

    version: '3'
    
    services:
      zookeeper:
        image: zookeeper
      
  • 关于Dubbo

    最近在做Dubbo,发现之前对Dubbo的理解有点误区。之前一直觉得Dubbo只是一套RPC框架,并没有一些云上微服务的那套(Netflix开源的那套)。最近仔细看了一下,发现其实每个方面都有涉及。

    1.集群容错

    Dubbo的集群容错是在dubbo-cluster模块中(https://github.com/apache/incubator-dubbo/tree/master/dubbo-cluster)。实现是在RPC层,当调用完成后会根据配置做出处理。有以下几种:
    1)失败自动切换,当出现失败,重试其它服务器。这个是默认策略
    2)快速失败,只发起一次调用,失败立即报错
    3)失败安全,出现异常时,直接忽略
    4)失败自动恢复,后台记录失败请求,定时重发
    5)并行调用多个服务器,只要一个成功即返回
    6)广播调用所有提供者,逐个调用,任意一台报错则报错
    这个和Hystrix是不一样的,Dubbo只是提供了容错,但是没有熔断。如果需要熔断,需要自己在消费者一端实现对应逻辑,和Dubbo框架无关。

    2.负载均衡

    Dubbo是支持负载均衡的,实现同样在 dubbo-cluster模块中(https://github.com/apache/incubator-dubbo/tree/master/dubbo-cluster)。也是在RPC层,具体的调用发生前完成。负载均衡的策略有以下几种:
    1)随机。这个是默认策略
    2)轮循
    3)最少活跃调用数
    这个和ribbon有点像,但是只提供了本地负载均衡,策略比ribbon少,不包含ribbon的缓存和拦截器特性。

    3.版本兼容

    如果出现不兼容升级时,可以用版本号特性过渡,