网页中可以使用的中文字体总是太少,而自定义的字体想要在网页中应用首先要解决的就是字体文件的大小问题,一个比较完整的中文字体一般都在2M-8M左右,相对来说很大了,如果放在网页端影响体验。所以一直以来的思路都是怎么样减小字体文件的大小,其实道理很简单:把不用的字剔除掉。
Fontmin就是这样一个工具,可以很方便的处理字体文件。
fontmin.png
试用了一下,还不错。

注册app id的时候手贱填的中国,然后今天下载microsoft remote desktop的时候竟然发现这玩意不能在中国区下载。网上有个教程操作一波:

1、首先电脑安装 iTunes 最新版(iTunes 啊,不是 App Store),我这里操作是 mac,如果 win 同学不好使,那只能去日苹果了(基本不可能);

2、登陆你的 Apple ID,依次点击 "账户-->查看我的账户-->切换国家 /地区";

3、进入贱贱的切换页面,然后一路确认到达支付信息页面,在最新版的 iTunes 中,支付方式是可以选择 Paypal 的;

4、点击 "Sign in Paypal",浏览器会跳到 Paypal 登录页面;

5、最稳的一波操作是: 不管你登没登录,不用去绑卡,直接点击浏览器左下角的 "返回 iTunes" 按钮(应该是英文的),此时你会发现 iTunes 刷新页面以后神奇的出现了 "None"...

6、接下来的操作就是找个美国信息,各种撸,最后别忘了充点钱消费一下

亲测可用~当然不需要充钱买什么礼品卡了~~
app.png

新的配置文件格式所说,使用antlr实现了一种我自定义的格式的解析,这个格式是这个样子的:

// Here is some comment
shared {
    string _baseUrl = "http://localhost:8080";
    string domain   = "riguz.com";
    bool ssl        = false;
    int version     = 19;
    int subVersion  = 25;
    float number  = 19.25;

    string urls         = ["http://localhost:8080", "http://riguz.com:8080"];
    string domains      = ["riguz.com", "dr.riguz.com"];
    bool sslArray       = [true, false];
    int versionArray    = [19, 25];
    float numberArray   = [18.01, 19.25, 20.23];
};

scope dev_db {
    string url = ${domain} .. ":3306/mysql";
    string user = "lihaifeng";
    int connections = 10;
    string password = "iikjouioqueyjkajkqq==";
    string domains = ${domains};
};

其实是一个k-v形式的文本文件,支持的基本类型有:字符串、布尔值、整数、小数、数组。定义的方法类似于Java或者C语言,string _baseUrl = "http://localhost:8080";前面会限定数据类型。如果要定义数组,则用bool sslArray = [true, false];这种形式。

然后使用scope区分不同的配置块。因为可能有些相同的配置会重名,这样我们利用不同的scope去区分就好了。考虑到有些配置中需要共同的变量的使用,所以定义了一个shared的scope,这个是写死的scope,其他scope中只能引用shared scope中的变量。

字符串连接使用..操作符。这样可以组装字符串。详细的实现可以在forks的子项目config中找到。

另外还实现了一个类似Play! Framework的路由定义文件的解析,长这个样子的:

controllers admin{
package com.riguz.forks.demo.controller
UserController
FileController
}

controllers {
package com.riguz.forks.demo.admin
UserController->AdminUserController
PostController
}

filters {
package com.riguz.forks.demo.filters
AuthorizationFilter
NocsrfFilter
}

routes admin {
+AuthorizationFilter
get  /users                 UserController.getUsers()
get  /users/:id             UserController.getUser(id: Long)
post /users                 UserController.createUser()
get  /users/:id/files/*name FileUserController.getFile(id: Long, name: String)
}
routes guest {
+NocsrfFilter
get /posts      PostUserController.getPosts()
get /posts/:id  PostUserController.getPost(id: String)
}

routes guest {
+NocsrfFilter
get /posts      PostUserController.getPosts()
get /posts/:id  PostUserController.getPost(id: String)
}

这个文件的解析也在上面的git中可以找到实现。通过Antlr可以很方便的把类似这样的文件解析出来,你甚至可以实现自己的领域语言。在实现过程中,遇到过一些问题,来说下问题吧。

首先是Antlr提供了Listener和Visitor两种方式,起初使用Listener来实现但是感觉比较麻烦,而使用Visitor则可以直接通过返回值来取得AST解析结果。我们解析一个文件的时候,是自顶向下的,一个个的去解析的,比如我们的配置文件的antlr语法定义如下:

script
    : shared? scope*
      EOF
    ;

其中shared又是这样的

shared
    : SHARED LBRACE (property SEMI)* RBRACE SEMI
    ;

也就是说 shared { k=v...} ;这样的形式,然后又开始到了property:

property
    : type NAME ASSIGN expression        #basicProperty
    | type NAME ASSIGN LBRACK
        expression? (COMMA expression)*
      RBRACK                             #arrayProperty
    ;

这样层层往下来看。然后解析的时候也是一样,我们首先有一个顶层的解析器:

public class ScriptVisitor extends CfParserBaseVisitor<Map<String, ScriptVisitor.Scope>> {
    private static final Logger logger = LoggerFactory.getLogger(ScriptVisitor.class);

    @Override
    public Map<String, Scope> visitScript(CfParser.ScriptContext ctx) {
        ...
    }

这个Visitor负责解析语法文件中定义的script块,然后解析里面的scope:

ScopeVisitor scopeVisitor = new ScopeVisitor(context);
        ctx.scope().forEach(scopeContext -> {
            logger.debug("Visit scope:{}", scopeContext.getText());
            Scope scope = scopeContext.accept(scopeVisitor);
            scopes.put(scope.name, scope);
        });

这样又实现一个ScopeVisitor去解析scope就好了。详细的实现就不多贴代码了。

另外一个问题是,对于错误的处理,我们在哪一步做?比如bool s = "123";这是错误的,我们其实可以在定义grammar的时候就避免这种错误来,但写起来会麻烦一些。目前的实现是在Visitor中去对逻辑进行判断的,前面只做语法检查就可以了。

参考:

JMH是一个测试Java程序性能的工具,比如我们现在要测试一下JDK8自带的Base64和另一个实现的性能,可以建一个工程:

group 'riguz'
version '1.0-SNAPSHOT'

apply plugin: 'java'

sourceCompatibility = 1.8

sourceSets {
    jmh
}
repositories {
    mavenCentral()
}

dependencies {
    jmhCompile project
    jmhCompile 'org.openjdk.jmh:jmh-core:1.21'
    jmhCompile 'org.openjdk.jmh:jmh-generator-annprocess:1.21'
    jmhCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

task jmh(type: JavaExec, description: 'Executing JMH benchmarks') {
    classpath = sourceSets.jmh.runtimeClasspath
    main = 'org.openjdk.jmh.Main'
}

然后写一个类:

@Benchmark
    @Warmup(iterations = 1, time = 5)
    @Measurement(iterations = 1, time = 5)
    public void encodeWithJdk() {
        final byte[] bytes = Dream.text.getBytes();
        byte[] encoded = Base64.getEncoder().encode(bytes);
        byte[] decoded = Base64.getDecoder().decode(encoded);
        assertTrue(Arrays.equals(bytes, decoded));
    }

    @Benchmark
    @Warmup(iterations = 1, time = 5)
    @Measurement(iterations = 1, time = 5)
    public void encodeWithBase64Codec() throws IOException {
        final byte[] bytes = Dream.text.getBytes();
        byte[] encoded = Base64Codec.encodeToByte(bytes, true);
        byte[] decoded = Base64Codec.decodeFast(encoded, encoded.length);
        assertTrue(Arrays.equals(bytes, decoded));
    }

其中Dream.text是一个很长的字符串。执行gradle的jmh task之后,可以得到结果

Benchmark                               Mode  Cnt   Score   Error  Units
Base64BenchMark.encodeWithBase64Codec  thrpt    5  15.296 ± 2.538  ops/s
Base64BenchMark.encodeWithJdk          thrpt    5  13.029 ± 1.563  ops/s

看样子要比JDK的实现强一丢丢,当然只是在上面的这种情况之下。差距并不大。

参考:

纯手工编译OpenJDK8。在Mac上尝试了一下,因为编译这玩意需要XCode4*,而现在XCode都升级到9了,虽然可以下载旧版的XCode,但试了一下貌似不太兼容。于是在Virtualbox中装lubuntu来编译一下玩玩。在Virtualbox使用NAT网络做端口转发的时候,竟然发现不支持主机的22端口(貌似是权限的问题),改为10240则Ok。

sudo apt-get install mercurial
sudo apt-get install lrzsz
hg clone http://hg.openjdk.java.net/jdk8u/jdk8u
cd jdk8u
sh get_source.sh

可能遇到的问题:

...
jdk:   abort: stream ended unexpectedly (got 8159 bytes, expected 29096)
...
WARNING: hotspot exited abnormally (255)
WARNING: jdk exited abnormally (255)
WARNING: nashorn exited abnormally (255)

解决办法是重试N次get_source.sh就可以了。

切换到最新的release tag:

hg tags
hg up jdk8u162-b12

安装依赖项:

sudo apt-get install build-essential
sudo apt-get install libasound2-dev libcups2-dev libfreetype6-dev
sudo apt-get install libx11-dev libxext-dev libxrender-dev libxtst-dev libxt-dev
sudo apt-get update
sudo apt-get upgrade

sudo add-apt-repository ppa:openjdk-r/ppa  
sudo apt-get update   
sudo apt-get install openjdk-7-jdk  
# refer https://github.com/hgomez/obuildfactory/wiki/How-to-build-and-package-OpenJDK-8-on-Linux

然后开始编译吧:

bash ./configure --with-target-bits=64 --with-freetype-include=/usr/include/freetype2/ --with-freetype-lib=/usr/lib/x86_64-linux-gnu

Ubuntu16下面必须指定freetype的路径,按照OpenJDK Build README里面所说,期望的路径是Expecting the freetype libraries under lib/ and the headers under include/. .而且特别指出:

*The build is now a "configure && make" style build
*Any GNU make 3.81 or newer should work
*The build should scale, i.e. more processors should cause the build to be done in less wall-clock time
*Nested or recursive make invocations have been significantly reduced, as has the total fork/exec or spawning of sub processes during the build
*Windows MKS usage is no longer supported
*Windows Visual Studio vsvars*.bat and vcvars*.bat files are run automatically
*Ant is no longer used when building the OpenJDK
*Use of ALT_* environment variables for configuring the build is no longer supported

因此有些文章上面设置ant, ALT_BOOTDIR等步骤不适用编译jdk8.为了提高编译速度,在虚拟机中设置了使用4个cpu核心。我们需要指定编译使用的cpu数来提高编译速度。

make clean
rm -rf build
bash ./configure --with-target-bits=64 --with-freetype-include=/usr/include/freetype2/ --with-freetype-lib=/usr/lib/x86_64-linux-gnu --with-jobs=4

这样配置完后的输出如下:

====================================================
A new configuration has been successfully created in
/home/riguz/jdk/jdk8u/build/linux-x86_64-normal-server-release
using configure arguments '--with-target-bits=64 --with-freetype-include=/usr/include/freetype2/ --with-freetype-lib=/usr/lib/x86_64-linux-gnu --with-jobs=4'.

Configuration summary:
* Debug level:    release
* JDK variant:    normal
* JVM variants:   server
* OpenJDK target: OS: linux, CPU architecture: x86, address length: 64

Tools summary:
* Boot JDK:       java version "1.7.0_95" OpenJDK Runtime Environment (IcedTea 2.6.4) (7u95-2.6.4-3) OpenJDK 64-Bit Server VM (build 24.95-b01, mixed mode)  (at /usr/lib/jvm/java-7-openjdk-amd64)
* C Compiler:     gcc-5 (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 version 5.4.0 (at /usr/bin/gcc-5)
* C++ Compiler:   g++-5 (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 version 5.4.0 (at /usr/bin/g++-5)

Build performance summary:
* Cores to use:   4
* Memory limit:   1997 MB
* ccache status:  not installed (consider installing)

这里有个performance的提示,就是ccache。但是这玩意安装之后貌似识别错误,索性不要了。然后就可以开始编译了

make images

尴尬的是虚拟机磁盘空间(10G)不够,幸亏可以动态调整一下

VBoxManage modifyhd ~/VirtualBox\ VMs/lubuntu/lubuntu.vdi --resize 20480
# 完了之后需要进入到系统,用分区工具调整分区大小,可能需要删除swap分区,扩展/后再重建swap分区
sudo apt-get install gparted
gparted

images目标会create complete j2sdk and j2re images,花费了大约10分钟时间:

----- Build times -------
Start 2018-04-11 10:03:36
End   2018-04-11 10:13:47
00:00:23 corba
00:00:15 demos
00:04:49 hotspot
00:01:02 images
00:00:15 jaxp
00:00:20 jaxws
00:02:26 jdk
00:00:28 langtools
00:00:13 nashorn
00:10:11 TOTAL
-------------------------

生成的文件在build/*/images中

riguz@riguz-VirtualBox:~/jdk/jdk8u/build/linux-x86_64-normal-server-release/images/j2sdk-image$ cd bin/
riguz@riguz-VirtualBox:~/jdk/jdk8u/build/linux-x86_64-normal-server-release/images/j2sdk-image/bin$ ls
appletviewer  javadoc       jdeps       jsadebugd     pack200      servertool
extcheck      javah         jhat        jstack        policytool   tnameserv
idlj          javap         jinfo       jstat         rmic         unpack200
jar           java-rmi.cgi  jjs         jstatd        rmid         wsgen
jarsigner     jcmd          jmap        keytool       rmiregistry  wsimport
java          jconsole      jps         native2ascii  schemagen    xjc
javac         jdb           jrunscript  orbd          serialver
riguz@riguz-VirtualBox:~/jdk/jdk8u/build/linux-x86_64-normal-server-release/images/j2sdk-image/bin$ ./java -version
openjdk version "1.8.0-internal"
OpenJDK Runtime Environment (build 1.8.0-internal-riguz_2018_04_11_10_03-b00)
OpenJDK 64-Bit Server VM (build 25.71-b00, mixed mode)

最后就是测试了。测试需要安装jtreg,注意一定要指定JT_HOME.

sudo apt-get install jtreg
cd test && make PRODUCT_HOME=`pwd`/../build/*/images/j2sdk-image JT_HOME=/usr/bin/jtreg all