分类: Gradle

  • Google的jib打包工具

    Google的jib打包工具

    今天看群里说起jib工具,就专门看了一下。地址:https://github.com/GoogleContainerTools/jib。

    jib旨在让开发者使用他们熟悉的工具更轻松地将 Java 应用程序容器化。

    来看看一般的应用如何容器化

    1. 编译构建出jar包或者war包
    2. 编写Dockerfile
    3. Docker 构建镜像到本地或者发布到仓库

    第一步还好说,构建本身由于maven和gradle的存在变得相当便利,如果是spring boot的应用,直接打包jar包,其他的用war插件打包war包就行了。

    第二步编写Dockerfile,大部分Dockerfile的内容都是相似的,准备对应的java环境,拷贝jar或者war,然后配置参数,指定启动脚本。

    第三步也很直接,直接执行docker build . -f Dockerfile

    那么Google专门开源的这个jib工具能够改善的点在何处呢?

    看看Google官方的说明:

    1. 简单 – Jib 采用 Java 实现,并作为 Maven 或 Gradle 构建的一部分运行。你不需要维护 Dockerfile ,甚至无需创建包含所有依赖项的 JAR 包。
    2. 快速 – Jib 利用镜像分层和注册表缓存来实现快速、增量构建。它读取你的构建配置,将应用分到不同的层中,只重新构建和推送发生变更的层。
    3. 可重现 –
  • 使用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/…

  • Java基准测试 — JMH

    JVM为了让Java代码更好更快的运行,做出了大量的改进和优化。虽然各种外部环境的变化让我们不再需要关注每一行代码的性能,但是也难免有需要自己衡量代码性能的时候。

    OpenJDK提供了JMH工具可以科学量化性能。

    对于一个已有的项目(比如Gradle项目),要使用JMH,首先要引入依赖

    compile group: 'org.openjdk.jmh', name: 'jmh-core', version: '1.20'
    compile group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.20'
    

    在IDE中enable annprocess,然后新建类

    import org.openjdk.jmh.annotations.Benchmark;
    import org.openjdk.jmh.runner.Runner;
    import org.openjdk.jmh.runner.RunnerException;
    import org.openjdk.jmh.runner.options.Options;
    import org.openjdk.jmh.runner.options.OptionsBuilder;
    
    public class App {
    
        @Benchmark
        public void method() {
    
        }
    
        public 
  • Java 9 新特性 — 模块化Jar包(Multi-release JAR files)

    Java 9 新特性 — 模块化Jar包(Multi-release JAR files)

    Java 9的特性有一个标号238的特性,链接如下:JEP 238: Multi-Release JAR Files

    这个特性是有一定争议的,并没有在开源框架中大规模使用。

    简而言之,多版本jar允许打包同一个类的多个版本,供不同运行时使用。 例如,如果在JDK 8上运行,Java运行时将使用该类的Java 8版本,但如果在Java 9上运行,它将使用Java 9特定的实现。同样,如果为Java 10版本构建版本,则运行时将使用它来代替Java 9和默认(Java 8)版本。

    多版本JAR的用例

    优化的运行时间

    开发应用程序时,开发人员不知道将在哪个运行时执行。 但是对于某些运行时,可以实现相同类的优化版本。 例如通过查看java.version可以判断当前运行环境,然后通过一些技术手段来调用不同的实现。多版本Jar可以直接从JVM层提供这种特性的支持,不再需要自己实现。

    冲突的API

    例如需要支持2个不同的运行时,但有一个已弃用的API。 目前有两种广泛使用的解决方案:

    最直接的方法是使用反射。 例如,可以定义一个VersionProvider接口,然后定义2个具体的类Java8VersionProvider和Java9VersionProvider ,它们是在运行时加载。 这个解决方案的一个变体是只有一个类,但是有不同的方法,通过反射访问和调用不同的方法。
    如果技术上适用,更高级的解决方案是使用方法句柄。

    还有一种常见的方案就是直接提供两个不同的jar文件,比如guava-jdk8和guava-jdk9。这个方法简单有效,但是管理这种代码库会带来一些难度。

    多版本JAR的使用

    Multi-release JAR files的关键在于MANIFEST.MF中,首先要声明

    Multi-Release: true

    然后提供不同版本的class文件,大概目录如下

    Gradle作为常见的JVM平台项目构建工具,虽然目前没有对于多版本Jar的直接支持,但是由于灵活性大,可以直接自动配置一个。…

  • Gradle生成独立运行包时出现Can’t Expand ZIP的问题

    有时候我们需要打包一个可独立运行的jar文件并分发出去,特别是桌面图形化程序。

    Gradle要打这种包是很方便的,网上有很多例子,比如以下这个

    jar {
      manifest { 
        attributes "Main-Class": "$mainClassName"
      }  
    
      from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
      }
    }

    又或者这样

    task fatJar(type: Jar) {
    	manifest {
            attributes "Main-Class": "$mainClassName"
        }
        baseName = project.name + '-all'
        from { 
  • Java项目中混合Scala

    虽然我并不怎么用Scala,但是经常接触到一些Scala的开源库。由于Scala本身的特性,所以对于使用者而言,懂不懂Scala并不重要。

    Spark是由Scala编写,可以只用Java调用,但是有时候需要自定义其中的一些组件的时候可能Java并不能做到,这个时候就需要写一些Scala的代码。

    原有的项目是Gradle管理的,而Gradle本身提供了对于Scala的支持,简单来看看Gradle Scala插件。在build.gradle中添加两行

    apply plugin: 'scala'
    ...
    compile 'com.databricks:spark-csv_2.11:1.4.0'

    然后在目录结构中添加一个scala,位置在这里scala-project

    然后直接开始写就行了,不得不说IDEA和Gradle工作的都很好,运行还是直接运行,其他什么都不用改,直接运行就行了。

    scala-spark

    参考资料

    https://docs.gradle.org/current/userguide/scala_plugin.html…

  • Spring Boot和Alpine Linux

    今天突然收到阿里云的短信,提示硬盘使用率超标,感觉特别奇怪。

    因为机器上只有两个数据库和一点应用,所有的资源啥的都放在七牛上的,不应该硬盘不够用才对。

    仔细看了一下发现是docker把空间吃了,速度把一些没用的image等等清理一番,硬盘使用率瞬间降到53%。

    机器上所有应用都是spring boot的,也就是java8环境的,之前使用的Docker Image基于ubuntu构建,大小500M+以上,突然想起之前听闻过Alpine Linux,小巧方便,说不定可以减少镜像大小。

    在网上找了一个找了一个基础镜像

    frolvlad/alpine-oraclejdk8:slim

    Dockerfile改造为

    FROM frolvlad/alpine-oraclejdk8:slim
    VOLUME /tmp
    ADD spring-boot-application.jar app.jar
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

    如果需要在镜像中构建,编译的话可能还需要bash支持

    FROM frolvlad/alpine-oraclejdk8:slim
    VOLUME /tmp
    RUN apk add bash
    RUN ./gradlew build -x test
    ADD spring-boot-application.jar app.jar
    RUN apk del bash
    
  • Travis CI更细致的配置

    Travis CI是一个很好的免费CI,和Github搭配也很合适,但是对于多Env的项目在具体的配置上有很多可以更细致处理的地方。

    第一个是coverage的检测,默认情况下一句简单的命令可以让它工作

    after_success:
      - ./gradlew cobertura coveralls

    但是这种情况,所有的Env都会上传测试覆盖率数据,虽然coveralls等平台会自己处理,但是还是希望能够节约时间,只上传一份。

    很多情况下我们都会配置每次代码更新就发布一个snapshot版本,对于多Env的情况,如果没有特殊处理,就会出现一次commit,发布多个版本的情况。

    Travis CI中有很多环境变量,有默认的,有和当前构建有关的,也有用户自己定义的,可以依靠这些变量做出判断,决定是否执行相关操作。

    比如在.travis.yml中添加

    after_success:
     - .travis/coverage.sh
     - .travis/deploy.sh

    然后对应的bash脚本中做判断

    #!/bin/bash
    
    if [[ $TRAVIS_PULL_REQUEST == 'false' && $TRAVIS_REPO_SLUG == 'varFamily/cocos-ui-libgdx' && $GDX_VERSION == '1.9.2' && $TRAVIS_BRANCH == 'master' ]];
    then
        echo 
  • 使用nebula facet插件扩展项目

    对于一般的Gradle项目而言,主要的代码有两块,一块是main,一块是test。

    但是很多时候我们需要的不只是main和test,比如一个小项目,如何随项目一起附带一个简单的demo。

    放在main里面自然不合适,放在test中感觉也很普通的单元测试会混淆。这种时候就希望能够在main和test之外再扩展一个demo出来。

    Netflix的Gradle插件就可以完成这个工作

    apply plugin: 'nebula.facet'

    然后在配置中添加

    facets {
        demo
    }

    这样我们就拥有了一个名为demo的块,目录为src/demo。然后编译任务名称为 demoClasses。

    这样我们就可以很轻松的新建一个跑demo的任务

    task demo(dependsOn: demoClasses, type: JavaExec) {
        main = "net.mwplay.cocostudio.ui.Runner"
        classpath = sourceSets.demo.runtimeClasspath
        standardInput = System.in
        workingDir = "$projectDir/src/demo/resources"
        ignoreExitValue = true
    }
  • 使用Travis CI的Env特性测试版本兼容性

    Libgdx一直没有官方的UI编辑器,而cocostudio作为编辑器的能力让人羡慕。

    有一个简单的办法就是使用cocostudio,导出项目,然后构建一个LIbgdx的runtime解析器,比如这个项目https://github.com/tianqiujie/cocostudio-ui-for-libgdx

    Libgdx有很多版本,如果能够长久保持一定版本的支持(比如最新版为1.9.2,可以考虑支持1.7.0及以上)。

    Travis提供了Env功能,可以配置多个环境变量,比如这里以版本为变量

    language: java
    
    jdk:
      - oraclejdk7
    
    env:
     - GDX_VERSION=1.9.2
     - GDX_VERSION=1.9.1
     - GDX_VERSION=1.9.0
     - GDX_VERSION=1.8.0
     - GDX_VERSION=1.7.0
    
    before_install:
     - chmod +x gradlew
    

    然后在Gradle配置中修改一下,改成先判断环境变量,如果没有就是用指定版本1.9.2,这样开发人员在本地不需要其他配置也可以让项目正常运行。

    ext {
        gdxVersion = System.env.GDX_VERSION != null ? System.env.GDX_VERSION : '1.9.2';
    }
    

    然后在Travis CI中的执行效果如下…