Gson 使用笔记

Gson 是 Google 发布的一个用于序列化和反序列化 json 的工具库,可以非常轻松的实现 json 到 java Object 的转变,也同样非常简单的可以将一个 Java 实例序列化为 json。

Gson 包中主要的类有 Gson, GsonBuilder, JsonParser 等等。

基本类介绍

JsonParser

JsonParser 是将 json 串解析成 JsonElement 的工具类。JsonParser 有三个 parse() 方法,分别接受不同类型的参数:

  • String
  • Reader
  • JsonReader

内部实现时使用 JsonReader 类进行解析。

基本使用

Gson 最基本的使用方法,无非就是 toJson()fromJson() 两个函数,对于简单类,可以使用如下方式:

String json = gson.toJson(target); // serializes target to Json
MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2

如果类中含有数组,会需要用到 toJson(Object, Type)fromJson(String, Type) 这两个方法:

Type listType = new TypeToken<List<String>>() {}.getType();
List<String> target = new LinkedList<String>();
target.add("blah");

Gson gson = new Gson();
String json = gson.toJson(target, listType);
List<String> target2 = gson.fromJson(json, listType);

属性重命名

Gson 默认情况下会使用 POJO 一致的属性名去解析和生成 json 字串,但是如果想要解析和生成的时候重命名字段,可以使用 @SerializedName 来重命名。

比如 json 中的字段叫做 email_address,而在 Java 类中,可以改为 emailAddress。这样,gson 在生成时会自动将 emailAddress 属性,改为 email_address

@SerializedName("email_address")
public String emailAddress;

如果 emailAddress 在 json 中还有其他的方式,也可以使用:

@SerializedName(value = "emailAddress", alternate = {"email", "email_address"})
public String emailAddress;

重新格式化序列化和反序列化的内容

在将 json 转化为 Java Object 的时候,可以自定义 Deserializer ,格式化其中的某一些字段,比如下面内容,将 dateOfBirth 修改了格式。

public class ActorGsonDeserializer implements JsonDeserializer<ActorGson> {
    private SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a");

    @Override
    public ActorGson deserialize(JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext)
            throws JsonParseException {
        JsonObject jsonObject = json.getAsJsonObject();

        JsonElement jsonImdbId = jsonObject.get("imdbId");
        JsonElement jsonDateOfBirth = jsonObject.get("dateOfBirth");
        JsonArray jsonFilmography = jsonObject.getAsJsonArray("filmography");

        ArrayList<String> filmList = new ArrayList<>();
        if (jsonFilmography != null) {
            for (int i = 0; i < jsonFilmography.size(); i++) {
                filmList.add(jsonFilmography.get(i).getAsString());
            }
        }

        ActorGson actorGson = null;
        try {
            actorGson = new ActorGson(jsonImdbId.getAsString(), sdf.parse(jsonDateOfBirth.getAsString()), filmList);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return actorGson;
    }
}



Gson gson = new GsonBuilder()
        .registerTypeAdapter(ActorGson.class, new ActorGsonDeserializer())
        .create();

忽略某些字段

使用 transient 关键字

在 java 序列化是,一旦变量被 transient 修饰,变量将不再是持久化的一部分,变量内容在序列化后无法获得访问。同样如果在使用 Gson 序列化 json 的时候,添加关键字 transient 同样,Gson 也会忽略 该字段:

private transient int id;

需要注意的是,如果一个 field 是 static 静态变量,gson 也会排除。Gson 在创建的时候可以使用 excludeFieldsWithModifiers 来指定排除的 field:

Gson gson = new GsonBuilder()
    .excludeFieldsWithModifiers(Modifier.STATIC, Modifier.TRANSIENT, Modifier.VOLATILE)
    .create();

使用 @Expose 注解保留关心的字段

最早不知道 transient 关键字的时候,看文档中只写了 @Expose 注解,但其实效果是一样的。使用 @Expose 注解来保留关心的字段,其他不需要的字段可以不注解,同样能够达到效果。

private int id; // 忽略 id
@Expose private String name;    // 保留 name

如果使用 @Expose 注解,那么则需要使用 GsonBuilder()

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();

自定义 ExclusionStrategy 规则

如果有更加复杂的排除规则,比如某一批 Field,或者指定的 Class 不需要 serialize ,可以使用 ExclusionStrategy 来自定规则。

import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.jutils.gson.entity.Car;
import com.jutils.gson.entity.PersonStrategy;

/**
 * ExclusionStrategy
 *
 * A strategy (or policy) definition that is used to decide whether or not a field or top-level class should be serialized or deserialized as part of the JSON output/input.
 */
public class ExcluStrategy implements ExclusionStrategy {
    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        // ignore id field
        if (f.getDeclaringClass() == PersonStrategy.class && f.getName().equals("id")) {
            return true;
        }
        return false;
    }

    @Override
    public boolean shouldSkipClass(Class<?> aClass) {
        if (aClass == Car.class) {
            return true;
        }
        return false;
    }
}

比如说上面这个,就忽略 PersonStrategy 类中的 Field “id”,还有 Car 类。如果使用这种方式,那么需要在构造 gson 时:

Gson gson = new GsonBuilder().setExclusionStrategies(new ExcluStrategy()).create();

实现自定义注解

如果熟悉了这个 ExclusionStrategy 就可以书写自己的注解。

注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}

AnnotationExclusionStrategy 类

public class AnnotationExclusionStrategy implements ExclusionStrategy {
    @Override
    public boolean shouldSkipField(FieldAttributes fieldAttributes) {
        return fieldAttributes.getAnnotation(Exclude.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> aClass) {
        return false;
    }
}

测试方法

@Test
public void annotationTest() {
    PersonAnnotation annotation = new PersonAnnotation(1L, "name", 10);
    Gson gson = new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();
    String s = gson.toJson(annotation);
    System.out.println(s);
}

将 JSON 中小写下划线转成驼峰

使用 GsonBuilder 来构造 Gson,然后传入 FieldNamingPolicy,这个方法接受很多参数,不仅可以做到将小写下划线转驼峰,还有其他很多功能。

Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.create();

FieldNamingPolicy 还有这些

IDENTITY
Using this naming policy with Gson will ensure that the field name is unchanged.
LOWER_CASE_WITH_DASHES
Using this naming policy with Gson will modify the Java Field name from its camel cased form to a lower case field name where each word is separated by a dash (-).
LOWER_CASE_WITH_UNDERSCORES
Using this naming policy with Gson will modify the Java Field name from its camel cased form to a lower case field name where each word is separated by an underscore (_).
UPPER_CAMEL_CASE
Using this naming policy with Gson will ensure that the first "letter" of the Java field name is capitalized when serialized to its JSON form.
UPPER_CAMEL_CASE_WITH_SPACES
Using this naming policy with Gson will ensure that the first "letter" of the Java field name is capitalized when serialized to its JSON form and the words will be separated by a space.

反序列化时默认值

某一些情况下在反序列化 json 到 Object 时,在某些字段 JSON 中缺失时,想要给 Object 提供一个默认值,但是 Gson 在处理原始类型时,比如 int 字段,如果缺失会自动赋值为 0,某些情况下是不符合预期的。Gson 在设定默认值时需要,在 Object 构造函数中初始化该字段,并且实现 InstanceCreator 接口。

public class RawDataInstanceCreator implements InstanceCreator<RawData> {

  @Override
  public RawData createInstance(Type type) {
    return new RawData();
  }
}

构造时传入:

Gson gson = new GsonBuilder()
    .registerTypeAdapter(RawData.class, new RawDataInstanceCreator())
    .create();

Gson 反序列化时类型不匹配

举一个比较通俗的例子就是,当一条 JSON 数据返回时,字段值是一个 Double,但是返回的时候是放在字符串中返回的。这个时候就需要用到 TypeAdapter。

class DoubleTypeAdapter extends TypeAdapter<Number> {
	@Override
	public void write(JsonWriter jsonWriter, Number number) throws IOException {
	  jsonWriter.value(number);
	}

	@Override
	public Number read(JsonReader jsonReader) throws IOException {
	  if (jsonReader.peek() == JsonToken.NULL) {
		jsonReader.nextNull();
		return 0D;
	  }
	  String result = jsonReader.nextString();
	  if ("".equals(result)) {
		return 0D;
	  }
	  return Double.parseDouble(result);
	}
}

然后创建 Gson 时:

    Gson gson = new GsonBuilder()
        .registerTypeAdapter(Double.class, new DoubleTypeAdapter()).create();

reference


2018-01-09 java , gson , json , google

小米路由器 3G 开启 SSH 安装 MT 工具箱

下面是小米路由器折腾记录,包括开启 SSH,然后安装 MT 工具箱,主要是为了其中的两个插件,一个是去广告,一个是 SS 代理,不过附带竟然发现了 frp 插件,开心啊。下面就是具体的记录。

小米路由器刷入开发版

下载 开发版,在后台点击上传安装开发版的 bin,然后等待重启,完成开发版安装。

小米路由器开始 SSH

小米帐号绑定小米路由器,设置路由器可正常上网,并使用手机版小米 WiFi 绑定路由器,然后在绑定小米账号的前提下,进入 https://d.miwifi.com/rom/ssh 这个网站,然后找到 SSH 登录的 root 密码,之后会用到。

工具包使用方法:小米路由器需升级到开发版 0.5.28 及以上,小米路由器 mini 需升级到开发版 0.3.84 及以上,小米路由器 3 即将支持。注意:稳定版不支持。 请将下载的工具包 bin 文件复制到 U 盘(FAT/FAT32 格式)的根目录下,保证文件名为 miwifi_ssh.bin; 断开小米路由器的电源,将 U 盘插入 USB 接口; 按住 reset 按钮之后重新接入电源,指示灯变为黄色闪烁状态即可松开 reset 键; 等待 3-5 秒后安装完成之后,小米路由器会自动重启,之后您就可以尽情折腾啦 :)

如果 Chrome 浏览器出现错误提示:”This site can’t be reached. d.miwifi.com refused to connect. ERR_CONNECTION_REFUSED”,需要手动将http替换为https

在使用 ssh 登录路由器之前确认路由器的 IP 地址,比如我下面例子中会使用 192.168.31.1 来举例。

刷入 MT 工具箱

MT 工具箱是目前第三方插件里面最为方便易用的插件集合

KMS 服务器,VSFTP 服务器,VPN 服务器,远程管理,ARIA2,Koolproxy 广告过滤,阿呆喵广告过滤,Shadowsocks,webshell, frp 服务

Misstar Tools 2.0 工具箱安装,经过上面的几个步骤,开启 SSH 之后,使用 ssh root@192.168.31.1 来连接路由器,使用之前获取的 SSH root 密码登录,进去之后 passwd 修改 root 密码,以方便下一次使用,然后直接执行如下代码,就能安装 MT 工具箱。

wget http://www.misstar.com/tools/appstore/install.sh -O /tmp/install.sh && chmod a+x /tmp/install.sh && /tmp/install.sh

卸载

wget http://www.misstar.com/tools/uninstall.sh -O /tmp/uninstall.sh && chmod +x /tmp/uninstall.sh && /tmp/uninstall.sh

安装 Shadowsocks 科学上网插件的方法

在开启 SSH,并且安装 Misstar Tools 工具箱的前提下,有两种方法可以安装 Shadowsocks 插件,第一种就是使用备份的文件,传入路由器之后运行安装,第二种直接在安装页面修改页面内容,推荐使用第二种方法。

方法一:使用文件安装

  1. 将压缩包中两个文件传到路由器,放在同一目录,名字不要改。
  2. 执行:chmod +x ./install_ss & ./install_ss add
  3. 刷新路由器后台,科学上网插件已经出现了。
  4. 卸载的时候执行:chmod +x ./install_ss & ./install_ss del

from: http://bbs.xiaomi.cn/t-13765387

方法二:修改页面 ID

使用 Chrome 打开 MT 插件管理页面,使用开发者工具,定位页面中任意一个 安装 按钮,然后找到代码中的 id="ftp" 字样,修改为 id="ss" ,然后点安装,成功后会回到 MT 工具箱首页,配置使用即可。

刷其他固件

按照上面官方的步骤操作完路由器就已经获取 root 权限了,再使用 ssh 工具连接路由即可,建议在进行下一步操作之前备份原版分区文件

root@XiaoQiang:~# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 07f80000 00020000 "ALL"
mtd1: 00080000 00020000 "Bootloader"
mtd2: 00040000 00020000 "Config"
mtd3: 00040000 00020000 "Bdata"
mtd4: 00040000 00020000 "Factory"
mtd5: 00040000 00020000 "crash"
mtd6: 00040000 00020000 "crash_syslog"
mtd7: 00040000 00020000 "reserved0"
mtd8: 00400000 00020000 "kernel0"
mtd9: 00400000 00020000 "kernel1"
mtd10: 02000000 00020000 "rootfs0"
mtd11: 02000000 00020000 "rootfs1"
mtd12: 03580000 00020000 "overlay"
mtd13: 012a6000 0001f000 "ubi_rootfs"
mtd14: 030ec000 0001f000 "data"

查看 U 盘挂载的位置命令:

df -h

U 盘一般是 /extdisks/ 开头,后面的可能不一样,我的是: /extdisks/sda4/

备份小米路由器 3G 原版分区到文件,每行是一条命令,分别执行,最后一条可能会报错,可不用理会,最有用的是 mtd0-mtd4

dd if=/dev/mtd0 of=/extdisks/sda4/rom/ALL.bin
dd if=/dev/mtd1 of=/extdisks/sda4/rom/Bootloader.bin
dd if=/dev/mtd2 of=/extdisks/sda4/rom/Config.bin
dd if=/dev/mtd3 of=/extdisks/sda4/rom/Bdata.bin
dd if=/dev/mtd4 of=/extdisks/sda4/rom/Factory.bin
dd if=/dev/mtd5 of=/extdisks/sda4/rom/crash.bin
dd if=/dev/mtd6 of=/extdisks/sda4/rom/crash_syslog.bin
dd if=/dev/mtd7 of=/extdisks/sda4/rom/reserved0.bin
dd if=/dev/mtd8 of=/extdisks/sda4/rom/kernel0.bin
dd if=/dev/mtd9 of=/extdisks/sda4/rom/kernel1.bin
dd if=/dev/mtd10 of=/extdisks/sda4/rom/rootfs0.bin
dd if=/dev/mtd11 of=/extdisks/sda4/rom/rootfs1.bin
dd if=/dev/mtd12 of=/extdisks/sda4/rom/overlay.bin
dd if=/dev/mtd13 of=/extdisks/sda4/rom/ubi_rootfs.bin
dd if=/dev/mtd14 of=/extdisks/sda4/rom/data.bin

首先,下载 Breed 刷入不死 breed,

下载地址:https://breed.hackpascal.net/ (搜索 breed-mt7621-xiaomi-r3g.bin)

用命令下载,在电脑终端中运行

wget https://breed.hackpascal.net/breed-mt7621-xiaomi-r3g.bin

将文件传入路由器

scp breed-mt7621-xiaomi-r3g.bin root@192.168.31.1:/tmp/

进入路由器

ssh root@192.168.31.1
cd /tmp
mtd -r write breed-mt7621-xiaomi-r3g.bin Bootloader

机器会重新启动,指示灯变蓝,确保电脑设置为自动获取 IP 地址,使用网线连接。刷入成功后,断掉电源,用东西顶住复位键不松开,然后再接上电源等待 10 秒左右放开复位键,浏览器输入 http://192.168.1.1 即可进行 Breed Web 恢复界面:

如果要刷入其他固件,打开 Breed Web 恢复控制台,点击左侧“固件更新”,钩选“固件”,选择固件,再点“上传”

更多内容可查看: http://www.right.com.cn/forum/thread-161906-1-1.html

小米路由器的目录结构

小米路由器基本上沿用了 Linux 的目录结构,但是也有一些区别,在用了一段时间之后发现某些目录被写满了导致一些第三方服务无法开启,也是很恼人了。这里及列一下这些目录的作用,以便于清理。

/       根目录
bin    二进制可执行命令
boot   bootloader 启动相关
data   用户数据文件
dev    设备文件,驱动等等
etc    配置文件
extdisks        外置硬盘挂载点
lib     共享库
mnt     临时挂载点
opt     可选程序安装点
proc    系统内存映射虚拟目录,可用来获取系统信息
root    系统管理员主目录
sbin    系统管理命令
sys
userdisk    路由硬盘(一般为内置)
usr         存放应用程序和文件
tmp        临时存放点
www         浏览器网页存放区

reference


2018-01-07 router , xiaomi , ssh , frp , shadowsocks

斐讯 k2p 刷机

以前也整理过一篇 TP LINK MR12U 刷 Openwrt ,路由器刷机和手机刷机异曲同工。不过刷机需谨慎,稍有错误就有可能导致硬件损坏。前些时候入手了一个斐讯的 K2P,

大致分为几步,第一步开启 telnet,刷入 uboot,然后通过 uboot 刷入新固件。

因为新版本固件 V22.7.8.2版本及之后 需要使用 特殊工具 才能开启 telnet ,所以先要使用这个工具来开启 telnet。然后再使用 a大 的脚本安装 breed。

Opboot 及 Breed进入方法:

如果你当前是官改或其他第三方固件,请在opboot或breed输入:

  1. 计算机设置为自动获取IP,计算机网线连接K2P的任一LAN口
  2. K2P断电,按住K2P复位键,K2P开电,按住10秒后放开
  3. 访问http://192.168.1.1,可刷入K2P的任何版本
  4. 重新启动后建议K2P恢复一次出厂设置;

官改版本

地址: http://www.right.com.cn/forum/thread-221578-1-1.html

官改版本比较稳定,占用内存小,同时也带去广告,SS/SSR,内网穿透等等功能

潘多拉固件

地址: http://www.right.com.cn/forum/thread-216468-1-1.html

潘多拉版本,同时也支持 SS/SSR,去广告,定时重启等等功能

梅林固件

地址: http://www.right.com.cn/forum/thread-255053-1-1.html

  • 支持adbyby及koolproxy广告屏蔽
  • 支持最新版本ss及S-S R客户端
  • 支持webshell、kms、ngrok内网穿透、wol网络唤醒
  • 支持ssh、用户脚本、硬盘休眠、指示灯控制等

Openwrt 固件

地址: http://www.right.com.cn/forum/thread-240730-1-1.html

K2P基于openwrt最新稳定版chaos_calmer的固件

Padavan

荒野无灯 http://www.right.com.cn/forum/thread-218704-1-1.html 这个固件和原版固件的性能对比可以参考这一篇 http://www.acwifi.net/3173.html

下载地址:http://files.80x86.io/router/rom/K2P/

k2p 全套其他的固件,可以参考这个帖子 http://www.right.com.cn/forum/thread-254947-1-1.html

最后总结下,其实大部分的教程都能在恩山找到,在斐讯的论坛里面,找精华贴,能过滤很多杂乱的信息。并且通过 Google 关键词搜索也能够很快的找到答案。


2018-01-06 k2p , 路由器 , router , openwrt , linux , phicomm

CollectionUtils

Apache commons 包中 CollectionUtils 常用方法介绍。

org.apache.commons.collections.CollectionUtils;

将所有元素添加到一个集合

addAll(Collection<C> collection, C[] elements) 将后面的元素添加到集合中,或者是添加一个非空元素 addIgnoreNull(Collection<T> collection, T object)

Collate (整理)两个列表

将两个有序的列表合二为一,使用 collate() 方法

List<Customer> sortedList = CollectionUtils.collate(list1, list2);

将一个列表中一种类型的对象转变为另一组对象

使用 collect() 方法

Collection<Address> addressCol = CollectionUtils.collect(list1, 
  new Transformer<Customer, Address>() {
    public Address transform(Customer customer) {
        return customer.getAddress();
    }
});

判断是否包含

containsAll(Collection<?> coll1, Collection<?> coll2) 判断是否 coll2 中所有元素都在 coll1 中

containsAny(Collection<?> coll1, Collection<?> coll2) 判断 coll2 中有没有至少一个元素在 coll1 中

CollectionUtils.isSubCollection(list3, list1) 可以使用该函数来判断两个集合是否是包含关系。

过滤元素

使用 CollectionUtils.filter 可以用来过滤 Collection 中不满足条件的元素。这个函数接收两个参数,第一个参数为列表,第二个参数为 Predicate 用来设置条件。

boolean isModified = CollectionUtils.filter(linkedList1, 
  new Predicate<Customer>() {
    public boolean evaluate(Customer customer) {
        return Arrays.asList("Daniel","Kyle").contains(customer.getName());
    }
});

filter() 函数是当 Predicate 返回 false 时移除元素;filterInverse() 函数是当 Predicate 返回 true 时移除元素。

如果想要获取过滤的结果,可以使用selectselectRejected 这两个函数。

判断集合是否为空

CollectionUtils.isEmpty(null): true
CollectionUtils.isEmpty(new ArrayList()): true
CollectionUtils.isEmpty({a,b}): false

判断集合是否不为空

CollectionUtils.isNotEmpty(null): false
CollectionUtils.isNotEmpty(new ArrayList()): false
CollectionUtils.isNotEmpty({a,b}): true

2个集合间的操作

集合a: {1,2,3,3,4,5} 集合b: {3,4,4,5,6,7}

CollectionUtils.union(a, b)(并集): {1,2,3,3,4,4,5,6,7}
CollectionUtils.intersection(a, b)(交集): {3,4,5}
CollectionUtils.disjunction(a, b)(交集的补集): {1,2,3,4,6,7}
CollectionUtils.disjunction(b, a)(交集的补集): {1,2,3,4,6,7}
CollectionUtils.subtract(a, b)(A与B的差): {1,2,3}
CollectionUtils.subtract(b, a)(B与A的差): {4,6,7}

更多的内容可以 Google: https://www.google.com/search?q=CollectionUtils

示例代码可以到 https://github.com/einverne/jutils/tree/master/src/test 来查看。


2018-01-03

2017 年读书笔记

又到一年的年末,2017 年对于我来说是变化最大的一年,这一年里完成了身份的转变,自此步入社会。这一年年初的时候定下目标年度 100 本书,看来又是无法完成了,现在看豆瓣的标注,也仅仅只有 30 本左右,距离目标甚是遥远。这里也不想再找借口,2017 年中,很大一部分时间共享给了王者荣耀和荒野行动,这两个手游占据了原本打散的碎片化时间。今年也一度差点荒废了 Kindle,Kinde10000 的消失,曾一度让 Kindle 在床头吃灰。

2017 年的读书记录如果再按照以前的分类划分的话,也没太多的变化,唯一增加的分类可能是增加了金融相关的一些书籍。那下面就总结一下好了。

小说

排在首位的永远是小说,在去年读过了大部分的东野圭吾的小说之后,今年似乎找不到主题,除了零星捡漏又看了几部东野圭吾的小说之外,其他作者的很少涉猎。不过今年读过的东野圭吾的小说中也还是有让我记忆比较深刻的几本:

  • 首先是这本《恋爱的贡多拉》,起初看到这本书的名字的时候,甚至一度怀疑这个作者到底是不是东野圭吾,仔细的查看封面和前言之后确认,这确实是东野圭吾的作品。这本书的文风与之前相比截然不同,这么轻松诙谐的文字,也只有东野圭吾这样的鬼才才能写出来吧。看这本书似乎就是看一个剧,东野圭吾通过滑雪场的缆车把书中几对情侣的各种“恩怨情仇”很诙谐的讲述出来,这样的作品直接拿去改编成剧本都丝毫不会有任何违和。不过这也正是这本书的毛病所在,太狗血的爱情桥段,太烂俗的现代都市爱情故事。不过我更欣赏东野圭吾在书中表述出来的不同人物形象和性格,都知道东野圭吾的推理小说中都是一些阴冷黑暗的角色,而这本不是,甚至这一本可以当成恋爱小说看,里面的人物形象都跃然纸上,不同主人公处理事务各不相同,也最终导致他们的结局不同,总之非常有趣。
  • 《东野圭吾的最后致意》这是一本随笔录,我记得以前也看过一部《我晃荡的青春》随笔录,东野圭吾从工程师,毅然辞职当起职业作家一路并不是一帆风顺,在经历了这么多的曲折之后,才有了今天的东野圭吾,从这本随笔录中也能看到当时努力写作的东野圭吾,幽默的文风一如从前。
  • 再就是《风雪追击》这一本不是那么出彩,滑雪场相关
  • 《悖论 13》 这算是科幻小说吗?我翻开豆瓣的评论,当时看完我写了,在这本小说中,我看到了好莱坞大片的元素,看到了《2012》,看到了《末日崩塌》,看到了《后天》这样的灾难大片的大场景,或许如果有一天有人把这个小说改编成电影,我也还是会去看一看的。其实这个故事很简答,出现了一个平行宇宙,里面的地球出现了各种灾难,一帮人自救互救的故事。
  • 《魔球》很早的作品了,平平
  • 《沉睡的人鱼之家》书中提出了一个值得思考的假设“如果一个人有着正常人的所有生命特征但长睡不醒那算是[[死亡]]吗?如果把一把刀插到植物人身上我算杀人吗?”
  • 《白马山庄杀人事件》或者叫 鹅妈妈旅舍杀人事件,关于这本书的出版,其实在《东野圭吾的最后致意》一书中也有提到,现在出版的名字是编辑给起的,原本的名字并不是白马山庄,看完其实也知道整本书和白马山庄并没有大多的关系。
  • 《疾风回旋曲》看完这本小说之后,也顺便把改编的电影也看了,东野圭吾是那么的喜欢滑雪嘛~最近太多关于滑雪场的故事了。
  • 《布谷鸟的蛋是谁的》 年中的时候看的,又是一本滑雪有关的,当然书中更多的讲得是关于孩子,血缘等等
  • 《拉普拉斯的魔女》说实话我不知道怎么归纳这一本,科幻?悬疑?就像当时在豆瓣写下的短评一样,我以为是讲述一个类似《达芬奇密码》这样保护秘密的故事,但其实故事更加复杂曲折。
  • 《平行世界·爱情故事》”带着科幻元素的爱情故事,即使记忆都不在了,爱情也依然不变。”现在看这段短评怎感觉和 《星际穿越》中及时时光穿梭,亲情不变这么像呢。
  • 《黎明之街》也是老早的作品了,tag 婚外情

专业书

今年看的专业书虽然数量有所增长,但是还是没达到目标。

  • 《REST API Design Rulebook》
  • 《Mastering Regular Expressions 3rd : Regular Expressions》
  • 《Mastering Nginx》
  • 《微服务设计》
  • 《Spring 实战(第 4 版)》

还有好多都零零落落没看完。

其他

  • 支付战争 : 互联网金融创世纪,一个产品到达一定瓶颈的时候,需要借助很多的营销策略让用户增长跟上一个层级,这本书就是 PayPal 当时早期员工写的如何帮助 PayPal 获取用户的回忆录
  • 增长黑客 : 创业公司的用户与收入增长秘籍,tag #营销 、获取用户 #用户增长
  • 《告白与告别》
  • 《冷浪漫》
  • 《细说汉字》
  • 《汉字构形学讲座》
  • 《基金投资入门与实战技巧》
  • 《一本书读懂黄金白银投资理财》
  • 《你今天真好看》
  • 《重新定义公司 : 谷歌是如何运营的》
  • 《孩子你慢慢来》

2017-12-31 book , reading , collection

Open Falcon 使用和介绍

OpenFalcon 是一款企业级、高可用、可扩展的开源监控解决方案,提供实时报警、数据监控等功能,由小米公司开源。使用 Falcon 可以非常容易的监控整个服务器的状态,比如磁盘空间,端口存活,网络流量等等。

最近有些监控需求所以看了一下其中涉及到概念。

一些基础概念

Open-Falcon,采用和 OpenTSDB 相似的数据格式:metric、endpoint 加多组 key value 的 tags,举两个例子:

{
    metric: load.1min,
    endpoint: open-falcon-host,
    tags: srv=falcon,idc=aws-sgp,group=az1,
    value: 1.5,
    timestamp: `date +%s`,
    counterType: GAUGE,
    step: 60
}
{
    metric: net.port.listen,
    endpoint: open-falcon-host,
    tags: port=3306,
    value: 1,
    timestamp: `date +%s`,
    counterType: GAUGE,
    step: 60
}

下面是一段 Python 上报数据的代码,其中涉及到的参数都是必须传的。

#!-*- coding:utf8 -*-

import requests
import time
import json

ts = int(time.time())
payload = [
    {
        "endpoint": "test-endpoint",
        "metric": "test-metric",
        "timestamp": ts,
        "step": 60,
        "value": 1,
        "counterType": "GAUGE",
        "tags": "idc=lg,loc=beijing",
    },

    {
        "endpoint": "test-endpoint",
        "metric": "test-metric2",
        "timestamp": ts,
        "step": 60,
        "value": 2,
        "counterType": "GAUGE",
        "tags": "idc=lg,loc=beijing",
    },
]

r = requests.post("http://127.0.0.1:1988/v1/push", data=json.dumps(payload))

print r.text

说明:

  • metric: 最核心的字段,监控指标名称(监控项),代表这个采集项具体度量的是什么,比如是 cpu_idle 呢,还是 memory_free, 还是 qps
  • endpoint: 标明 metric 的主体(属主),比如 metric 是 cpu_idle,那么 endpoint 就表示这是哪台机器的 cpu_idle,一般使用机器的 hostname
  • timestamp: 表示上报该数据时的 unix 时间戳,注意是整数,代表的是秒
  • value: 代表该 metric 在当前时间点的值,float64
  • step: 表示该数据采集项的上报周期,这对于后续的配置监控策略很重要,必须明确指定。
  • counterType: 是 Open Falcon 定义的数据类型,取值只能是COUNTER或者GAUGE二选一,前者表示该数据采集项为计时器类型(累加值),后者表示其为原值 (注意大小写,这个值是一个波动的值)

      - GAUGE:即用户上传什么样的值,就原封不动的存储
      - COUNTER:指标在存储和展现的时候,会被计算为 speed,即(当前值 - 上次值)/ 时间间隔
    
  • tags: 监控数据的属性标签,一组逗号分割的键值对,对 metric 进一步描述和细化,可以是空字符串。比如 idc=lg,比如 service=xbox 等,多个 tag 之间用逗号分割

说明:这 7 个字段都是必须指定

Metric

Metric 监控指标,时序数据。

包括不同类型:

  • counter 数值单调递增
  • guage 数值每次更新

endpoint

endpoint 通常来指服务器节点。

Falcon 的接口概念

在 Falcon 中有这样几个不同的接口

Meter

Meter 用来累加,可以输出累加和,变化率。

Gauge

Gauge 用来记录瞬时值,支持 int 64, float 64 类型

Histogram

Histogram 用来计算统计分布,输出最小值,最大值,平均值,75,95,99 百分比等等。

如何在上报中断时报警

最近遇到的需求就是如果一段时间内,OpenFalcon 没有收集到数据,也就是 agent 没有采集到数据,程序挂了,或者没有执行,那么就报警。在最开始的时候查看了一下 OpenFalcon 报警函数

all(#3): 最新的 3 个点都满足阈值条件则报警
max(#3): 对于最新的 3 个点,其最大值满足阈值条件则报警
min(#3): 对于最新的 3 个点,其最小值满足阈值条件则报警
sum(#3): 对于最新的 3 个点,其和满足阈值条件则报警
avg(#3): 对于最新的 3 个点,其平均值满足阈值条件则报警
diff(#3): 拿最新 push 上来的点(被减数),与历史最新的 3 个点(3 个减数)相减,得到 3 个差,只要有一个差满足阈值条件则报警
pdiff(#3): 拿最新 push 上来的点,与历史最新的 3 个点相减,得到 3 个差,再将 3 个差值分别除以减数,得到 3 个商值,只要有一个商值满足阈值则报警
lookup(#2,3): 最新的 3 个点中有 2 个满足条件则报警

这一下子就懵了,报警触发的条件都是根据最近上报的几个点的阈值来触发的,而我的需求可能是一段时间内根本没有上报数据。

然后仔细查看文档之后,发现 OpenFalcon 有一个 Nodata 配置,Nodata 的配置正好解决了上面的需求,当机器一段时间内中断上报时,Nodata 配置会上报一个指定的值,然后报警函数就能够根据 Nodata 上报的值来报警。

Nodata 的配置在 OpenFalcon 的后台,在 Nodata 页面添加 Nodata ,填写

  • name nodata 的名字,标示什么中断了
  • endpoint 选择 Endpoint ,机器列表,一行一个
  • metric 指定 metric
  • tags 指定 tags
  • type 暂时只支持 GAUGE
  • 周期 秒,与原始监控指标一致
  • 上报中断时补发值

当自定义上报中断的时候 Nodata 就会补发,通过补发的值,比如正常的取值是 >0 的正数值,那么补发的值可以写上 -1 ,然后通过最近连续的三个 -1 来触发报警。

Histograms

在服务端接收到打点数据时,在报表中会有一些内置的指标,比如说 75-percentile, 95-percentile, 99-percentile, 999-percentile,CPS-1-min, CPS-5-min, CPS-15-min 等等指标。

对于 percentile 的指标,含义如下:

  • 所有采样数据中,处于 75% 处的数值
  • 所有采样数据中,处于 95% 处的数值
  • 所有采样数据中,处于 99% 处的数值

对于这些指标,还有 min, max, mean 这些指标,这些都比较简单不再赘述。

而另外一种, falcon 中叫做 Meter, 有两种

  • sum 是事件发生的总数
  • rate 是 Falcon 上报周期(60s) 内,事件发生的频率,单位 CPS

项目的起源

最近看了一篇文章 介绍了 Open Falcon 项目的起由,这里面提到了 Open Falcon 为了解决 zabbix 的一些问题而被提出

  • 性能瓶颈,数据量大,zabbix 使用的 MySQL 写入瓶颈
  • 多套 zabbix 管理成本高
  • 监控系统不统一

reference


2017-12-29 monitor , log , open-falcon , warning

argparse的使用介绍

argparse 模块可以轻松的写出友好的命令行交互,让使用者轻松定义命令行参数及使用参数。

简单使用

首先创建一个parser

首先通过 argparse 来创建一个 ArgumentParser 对象:

parser = argparse.ArgumentParser(description='Process some integers.')

ArgumentParser 对象会保存命令行基本的信息。

再添加参数

构造好了 ArgumentParser 对象之后使用 add_argument() 来往其中添加参数。

>>> parser.add_argument('integers', metavar='N', type=int, nargs='+',
...                     help='an integer for the accumulator')
>>> parser.add_argument('--sum', dest='accumulate', action='store_const',
...                     const=sum, default=max,
...                     help='sum the integers (default: find the max)')

这些信息都被保存在对象中,直到 parse_args() 函数被调用。 integers 参数保存了一个或者一个以上列表的int值, accumulate 参数则会根据传入的参数选择 sum() 或者 max() 方法。

解析并使用参数

ArgumentParser 通过 parse_args() 来解析和使用参数

>>> parser.parse_args(['--sum', '7', '-1', '42'])
Namespace(accumulate=<built-in function sum>, integers=[7, -1, 42])

关于 ArgumentParser 对象更多的使用方法可以参考官方文档

参数动作

argparse内置6种动作可以在解析到一个参数时进行触发:

  • store 保存参数值,可能会先将参数值转换成另一个数据类型。若没有显式指定动作,则默认为该动作
  • store_const 保存一个被定义为参数规格一部分的值,而不是一个来自参数解析而来的值。这通常用于实现非布尔值的命令行标记
  • store_ture/store_false 保存相应的布尔值。这两个动作被用于实现布尔开关
  • append 将值保存到一个列表中。若参数重复出现,则保存多个值
  • append_const 将一个定义在参数规格中的值保存到一个列表中
  • version 打印关于程序的版本信息,然后退出

参数群组

argparse能将参数定义组合成“群组”。默认情况下是使用两个群组,一个是选项的群组,另一个是必须的与位置相关的参数群组。

在基础解析器中使用add_argument_group()来创建一个“身份认证”群组,然后逐个添加身份认证相关的选项到该群组。

import argparse
parser = argparser.ArgumentParser(add_help=False)
group = parser.add_argument_group('authentication')
group.add_argument('--user', action="store")
group.add_argument('--password', action="store")

互斥选项

定义互斥的选项是选项分组特性的一个特例,使用add_mutually_exclusive_group()而不是add_argument_group()

import argparse
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('-a', action='store_true')
group.add_argument('-b', action="store_true")
print parser.parse_args()

可变形参列表

你可以配置单个参数的定义使其能够匹配所解析的命令行的多个参数。根据需要或期望的参数个数,设置nargs为这些标识值之一:

含义
N 参数的绝对个数(例如:3)
? 0或1个参数
* 0或所有参数
+ 所有,并且至少一个参数

参数类型

argparse将所有参数值都看作是字符串,除非你告诉它将字符串转换成另一种数据类型。add_argument() 的type参数以一个转换函数作为值,被ArgumentParser用来将参数值从一个字符串转换成另一种数据类型。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-i', type=int)
parser.add_argument('-f', type=float)
parser.add_argument('--file', type=file)
try:
    print parser.parse_args()
except IOError, msg:
    parser.error(str(msg))

要想将一个输入参数限制为一个预定义集中的某个值,则使用choices参数。

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--mode', choices=('read-only', 'read-write'))
print parser.parse_args()

values的类型取决于nargs的值。如果该参数允许多个值,则values会是一个列表,即使其仅包含一个列表项。

reference


2017-12-28 python , argparse , linux , command

Mastering Google Photos

前段时间 Google 1T 空间到期,这几年积累的邮件,照片,Drive 中的内容已经超过了了 20+GB,突然一下子 Google 的空间就满了,Gmail 提醒我空间不足可能无法接收邮件,Drive 无法上传文件,而我查看了一下空间占用,发现 Google Photos 占用超过了 12G,网上搜了一圈,发现 Google Photos 提供一键压缩,然后瞬间就可以继续无限存储了。

因此诞生了这样一篇文章,来看看现在的 Google Photos 怎么来管理照片。其实到现在为止我还是很怀念被关闭的 Picasa,虽然数据无缝的同步到了新的 Google Photos 中,但是这个产品层面的转变,让我很多习惯都改变了,并且以前很多好用的功能都无法找到代替品。

查看 Google Photos 占用 Google 空间

这里不仅能看到 Google Photos https://drive.google.com/#quota

一键压缩 Google Photos 中的照片内容

在桌面网页版设置中 https://photos.google.com/settings,可以点击 RECOVER STORAGE 按钮来一键将 Google Photos 中以前保存的无压缩的照片压缩到 Google 支持的高清不占用空间的格式。Google Photos 在计算空间时 16 megapixel 及以下的照片不算空间,而一般 iPhone 7 拍的照片也只有 12 megapixel,所以完全不用担心损失画质,并且我曾经对比过Google Photos 压缩过的图片和原片,肉眼无法看出差别。这个操作不会影响存放在 Google Drive 中的原图,但是会压缩曾经上传到 Blogger,Google Map,Panoramio,Google+ 和 Google Hangouts 中大于 16 megapixels 的图片。

图片外链

Google 一路改过来,Picasa -> Google+ Photos -> Google Photos,外链是原来越难看。

Picasa

Picasa 链接

Google Photos

https://lh3.googleusercontent.com/7wqn4oULz6lckUdJbPZzT094xyJiDewL03QS5BZk5kz9c5vP_wvk7fGxSOtxYvAG9N98blJGfZTAdweG_rYnC7sLIrjBAJfX0uIsub_P6waW9AzWmhM0M23BRRVOjNVn52CZIpKbmxMdr9jyjI27zdFCKJb7ZenbDtsFhkr-O7gaXcKuQkQnXJPOI3I8rgYcLGDa3vggdeyCVneimFmRCWfRX75LtJegBshiF7Jy4-fE6slJalAUqtwRAmBck2kaAofuZOHlpAmSsmaNPuaIWoMuTu_AgX1FU1Xx0f9HONI-F0awr0vPl0en1klvBMWXS8RbW5GHrbOrDXtQbpveJ-KnfXPmtTkjxF7vakMMUvWugpg2eohkWh354ndUoqooOP3LrVc-Q8TjrP_9xxkJLOGCdK-Exh2mhXKiTnz_89iPD5dfI27ZmZdY4cVblw2qAOtJ-KVuGXT6oQy0mjttwuiVJONCz-Hl2Tvuncl9ZTCq0XvOBx6wJWDZELxtust5CXIsrsO4L5cSYlxNG_CBXTuR=s945-no

API 导出

https://lh3.googleusercontent.com/bhmJ-XtoL1wMCw3Q2YA0Ff_L8m52OgFbxYfalNjLO22e=s0

我惊讶于同一个照片,在不同的产品中的地址都是不同的,当然基于产品的逻辑倒也不能说什么,但是 Google Photos 的链接也太难看了。

对比

下面是很早之前发过的帖子

其实到现在Google Photos的功能都不及Picasa

  • 将相册分享给特定用户,用户需登陆账号才可观看
  • 相册标签分类功能
  • 相片外链,在Google Photos 中生成的图片外链很奇怪,远没有Picasa中的直观
  • 相册管理,Picasa 中很容易进行相册管理,而我所熟悉的正是相册管理而不是时间序管理,因为我经常搜不到特定的照片,而如果利用相册我能够非常方便的联想起相片所在的上下环境

而Photos自带的功能看似神奇却也是Picasa自带功能的延伸

  • 人脸检索,早在Picasa时代,人脸管理就已经让我很惊奇了。
  • 搜索,虽然现在利用大数据让我们能够检索照片中的内容,而这个检索却经常让我无法找到特定内容,似乎这个功能很鸡肋,我更加愿意利用tag来标注图片
  • 自动故事、历史照片和自动修图,看起来挺棒的feature,使用过程也是挺棒的,却无意中又捣乱了相册的管理,导致相册乱七八糟,曾经 Google+ Photos的修图功能让我很是惊奇,几乎已经可以代替本地简单修图,并且自带简单滤镜,然而升级之后我就再没有打开过,一是加载缓慢,二是最初的时候完全无法找到该工具。
  • 多人相册管理,这个Google Photos刚更新的神奇功能其实早就被Picasa实现了,对于 Picasa 相册来说可以添加特定用户让其拥有相册管理权限,其中当然包括上传的功能。

其实最后总结一下就是Google 搞了一个阉割的相册管理工具,未成熟却先发布,说是为了产品线统一其实最后就是把阉割了的Picasa直接放出来给用户用了。

Google Photos 改变了我整理照片的方法

最早的时候,从来都是每一次一个相册一个相册管理我的照片,按照日期和活动名字来命名每一个相册,如果分享的话直接同步到 Picasa 然后分享整个相册,然而移动互联网发展的今天 Google Photos 会自动备份手机中的所有图片,默认会给每一天新建一个相册,虽然现在 Google Photos 中间不会显示每天的相册名字,但是如果在其他地方用API来引用 Google Photos 中的内容会异常头疼,所以现在我已经放弃了。

最原先的时候我会精心挑选几张highlight 的照片来作为一个精选照片,而现在 Google 把所有的不管好坏的照片都备份了,以前 Google+ Photos 还能够自动挑选一些比较精华的照片,但是说实话那个照片也是比较鸡肋的,所以现在的 Google Photos 我都是在备份之后,挑选一些照片 Snapseed 或者 VSCO 再修一修上传。


2017-12-26 经验总结 , Picasa , Google , Google Photos

h5ai 目录列表程序

h5ai 自己的介绍说自己的是为HTTP WEB服务设计的一款现代化的文件索引,主要面向文件,提供了现代化的可视化界面。他是一款功能强大的文件目录列表程序,由德国开发者 Lars Jung 主导开发,提供多种文件列表呈现方式,支持多种主流WEB服务器,可以在线预览文本,图片,音频,视频。

依赖:PHP 5.5+ and works fine with Apache httpd, lighttpd, nginx and Cherokee

在没有 h5ai 之前,我都使用 nginx 的自带的显示文件列表配置

location / {
    audoindex on;
}

不过不管是 Apache 还是 Nginx 提供的文件列表都是非常简易的,只会显示当前文件夹下的文件,如果都是压缩文件还好,遇到一些多媒体,图片,音频,视频等等就会有一些不便。

安装

h5ai 的安装非常方便,下载,解压,配置 Nginx,配置 DNS,访问即可,如果需要高级功能,可以再配置,主要的配置修改

  • 安装php7.0, sudo apt install php7.0
  • 修改 index 添加 h5ai 的地址, index index.html index.htm /_h5ai/public/index.php;
  • 增加 php 的配置

配置 Nginx,在 /etc/nginx/sites-available/ 下创建 drive.einverne.info

server {
	listen 80;
	listen [::]:80;

	# SSL configuration
	#
	# listen 443 ssl default_server;
	# listen [::]:443 ssl default_server;

	# Add index.php to the list if you are using PHP

	root /var/www/drive.einverne.info/html;
	index index.html index.htm /_h5ai/public/index.php;
	server_name drive.einverne.info;

	location / {
		# First attempt to serve request as file, then
		# as directory, then fall back to displaying a 404.
		# autoindex on;
		try_files $uri $uri/ =404;
	}

	# deny access to .htaccess files, if Apache's document root
	# concurs with nginx's one
	#
	#location ~ /\.ht {
	#	deny all;
	#}
	location ~ \.php$ {
		include snippets/fastcgi-php.conf;
		fastcgi_pass unix:/run/php/php7.0-fpm.sock;
	}
}


# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#
#server {
#	listen 80;
#	listen [::]:80;
#
#	server_name example.com;
#
#	root /var/www/example.com;
#	index index.html;
#
#	location / {
#		try_files $uri $uri/ =404;
#	}
#}

再将域名的 DNS 配置 A 记录解析到 VPS

在配置的 root 目录 /var/www/drive.einverne.info/html

wget https://release.larsjung.de/h5ai/h5ai-0.29.0.zip
unzip h5ai-0.29.0.zip

然后刷新浏览器即可。 哦对了要确保安装了 PHP 7.0 的哈。

配置

h5ai 安装完成之后,可以到 domain/_h5ai/public/index.php 查看 h5ai 的相关信息,默认密码为空。页面中可以查看当前 h5ai 开启的选项。

Ubuntu 16.04 以上

apt-get -y install zip
apt-get -y install ffmpeg   # 视频缩略图
apt-get -y install imagemagick   # PDF 缩略图

增加密码保护

编辑文件 /_h5ai/public/index.php ,在底部增加:

function auth ()
{
    $valid_passwords = array ("账号" => "密码");
    $valid_users = array_keys($valid_passwords);

    $user = $_SERVER['PHP_AUTH_USER'];
    $pass = $_SERVER['PHP_AUTH_PW'];

    $validated = (in_array($user, $valid_users)) && ($pass == $valid_passwords[$user]);

    if (!$validated) {
      header('WWW-Authenticate: Basic realm="My Realm"');
      header('HTTP/1.0 401 Unauthorized');
      die ("Not authorized");
    }
}

在头部 <?php 的下一行,增加

auth();

options.json 开启更多功能

位于 _h5ai/private/conf 目录下。

打包下载: 搜索 “download” 127 行,enabled 由 false 改为 true。

文件信息及二维码: 搜索 “info” 185 行,enabled 由 false 改为 true。

默认简体中文: 搜索 “l10n” 202 行,enabled 由 false 改为 true。

文件及文件夹多选: 搜索 “select” 323 行,enabled 由 false 改为 true。

还有二维码等等功能,看一眼配置基本就能明白。

修改 domain/_h5ai/public/index.php 页面的默认密码:

首先生成自定义 sha512 密码:http://md5hashing.net/hashing/sha512 然后搜索 “passhash”,大概第 10 行,将其密码改成自己生成的。

注意

如果使用 LNMP 一键安装的环境,可能需要修改 php 配置,vim /usr/local/php/etc/php.ini 搜索 scandir、exec、passthru,将其从被禁用的函数中删除。


2017-12-20 linux , php , nginx , h5ai , file

最有用的 Chrome 快捷键提高数倍效率

本文总结一下我常使用的 Chrome 默认快捷键,Chrome 默认的快捷键已经非常全面,几乎可以不用鼠标操作浏览器的一切行为,我总以为使用不管是使用鼠标还是手势来进行网页浏览是非常耗时的一件事情,如果能够像 Vim 的哲学一样,不用离开键盘来浏览网页,不仅提高的是效率,也能够培养起,不再做内容的消费者,而是做内容的产生者这样的意识。

首先需要申明一下的是这些都是在 Linux 环境下,Mac 系统及 Windows 系统可能略微有差别,具体请查阅 Google 官方的 Help

下面是 Chrome 自身的一些常用默认快捷键

MacOS 快捷键 Linux 快捷键 作用
Cmd + [ Alt + Left back current page
Cmd + ] Alt + Right Forward current page
Cmd + y Ctrl + h 在新标签页打开历史记录
Cmd + Shift + j Ctrl + j 在新标签页打开下载记录
  Ctrl + k 将光标定位到 Omnibox ,也就是 Chrome 自定搜索
Cmd + l Ctrl + l 将光标快速定位到地址栏,可以输入地址,或者直接进行 Google 搜索
Cmd + t Ctrl + t 新标签页
Cmd + d Ctrl + d 收藏当前页面
Cmd + Shift + t Ctrl + Shift + t 重新打开关闭的标签页
Cmd + w Ctrl + w 快速关闭当前标签页
Cmd + p Ctrl + p 打印 或者 保存为 PDF
Cmd + n Ctrl + n 新开浏览器窗口
Cmd + Shift + n Ctrl + Shift + n 新开隐身窗口
Cmd + r Ctrl + r 或者 F5 刷新
Cmd + Option + u Ctrl + u 查看源码
  Ctrl + Shift + i 审查元素
  Ctrl + Shift + j 开发者工具
  Ctrl + Tab 切换到下一个打开的标签页
  Ctrl + Shift + Tab 切换到上一个打开的标签页
  Ctrl + 单击 在新标签页打开
Cmd + 1/2/3/4 Ctrl/Alt + 1/2/3/4 快速切换标签页
Cmd + f Ctrl + f 页面内搜索

以上就是最最常用的到的一些快捷键,容易忽略的我使用加粗显示了。

Google 隐藏的快捷方式

我们都知道 Google 的地址栏其实非常强大,Google 把它叫做 Omnibox,不仅可以预测下一个输入,也能够使用之前的记录保存多个搜索引擎。比如我经常使用的一种方式是输入 book.douban.com 然后因为豆瓣图书已经是 Chrome 认为的一个搜索引擎,所以再输入完毕之后 Tab,此时会进入搜索,然后输入图书的名字,回车,就会直接在豆瓣图书中搜索该图书。

对于默认使用 Google 作为地址栏的搜索引擎,那么直接输入回车就会在 Google 进行搜索。默认情况下只要访问过的网站提供搜索功能 Chrome 会自动记录这些网站到 search engines 中,可以在 chrome://settings/searchEngines 查看到。我们都知道 Chrome 中默认的 Google 搜索通常都带一系列的参数,默认情况下这些参数都不大必要,所以我会定义我自己的搜索 query,新增一个搜索引擎,其中的三个输入框分别填入

MyGoogle
google.com__
https://www.google.com/search?q=%s

然后在设置 MyGoogle 作为默认搜索引擎即可。使用同样的方法可以添加自己的专属搜索引擎,比如搜索 Gmail,Google Drive 等等。

然而 Omnibox 其实不仅能够提供 Tab Search 功能,它远比用户想象的要强大的多,甚至可以把它想象成 Google 的搜索栏。这里举一些例子

  • 数学计算,Omnibox 输入之后直接会有结果
  • 查询天气,输入 Weather Beijing 不用回车会直接显示结果
  • 查字典, 输入 define + word 会直接显示词典结果
  • 翻译, 输入 translate + word or phrase
  • 汇率转换 currency dollar to rmb

拖拽搜索

一个很常见的场景就是在网页浏览中看到一个新的词,想要搜索这个词,有很多方法可以实现,右击搜索也行,复制粘贴到新页面回车也好,不过最简单的方式就是直接选中然后拉到 Tab 位置。这时 Chrome 会使用默认的搜索引擎在新标签页中进行搜索。

长按返回键浏览当前页面历史

在网页中我们可能经常在一个又一个链接中迷失方向,这个时候不用担心,Chrome 记录了每一个浏览过的页面,长按返回键,可以看到一长串的浏览记录,点击其中的一个能看到 Chrome 非常快速的加载了,甚至断网也能够加载,因为 Chrome 已经缓存了访问过的页面。

几个非常常见的 internal URLs

Chrome 自带一些内部页面,这些页面有些非常常见的用来配置,管理插件,查看历史等等作用的,但更多的是开启一些真正测试功能的,一些 Chrome 内部的数据也能够看到,比如 Omnibox 中根据预测出现的地址等等。我们可以通过 chrome://chrome-urls/ 这个页面来查看所有 Chrome 自带的内部地址。

  • chrome://help
  • chrome://extensions
  • chrome://history
  • chrome://bookmarks/
  • chrome://omnibox/
  • chrome://chrome-urls/
  • chrome://predictors

插件的快捷键

Vimium

在 Chrome 中使用 Vim 下的快捷键,现在市场上也有不少的在 Chrome 下使用 Vim 快捷键的插件了,但 Vimium 已经用了很多年,没有遇到太大的问题。

下载地址:https://chrome.google.com/webstore/detail/vimium/dbepggeogbaibhgnhhndojpepiihcmeb

使用 shift + / 查看所有快捷键,绝大部分都是 Vim 的快捷键,不在赘述。

页面内跳转

快捷键 作用
j 向下
k 向上滚动
d 向下翻页
u 向上翻页
f 显示快捷键导航
F 新标签页打开此链接
H 回退
L Forward
b 弹出 vomnibar 来打开收藏夹中的网址
B 弹出 vomnibar,但在新标签页打开选中的网址
o 打开搜索工具栏,在收藏夹,历史记录中搜索,如果没有匹配则直接使用默认的搜索引擎搜索,1.66 版本后 o 可搜索打开的标签页
O 功能和 o 一致,区别为在新标签页中显示搜索结果
r 刷新
yy 复制当前页地址
gi 将光标放到第一个输入框中,在 Google 搜索结果页面非常有用
gg 顶部
G 导航到页面底部

标签页

快捷键 作用
J 或 gT 左边的标签页
K 或 gt 右边的标签页
t 创建新标签页
x 关闭当前的标签页
X 恢复刚刚关闭的标签页
g0 跳转到第一个标签页
g$ 跳转到最后一个标签页

其他有用快捷键

快捷键 作用
Ctrl + Shift + W 关闭当前浏览器窗口

外延

虽然写了这么多 Chrome 常用的快捷键,但如果使用鼠标,还有一个非常常用的,那就是鼠标中键点击关闭标签页。


2017-12-19 chrome , shortcut , efficiency

电子书

本站提供服务

最近文章

  • 从 Buffer 消费图学习 CCPM 项目管理方法 CCPM(Critical Chain Project Management)中文叫做关键链项目管理方法,是 Eliyahu M. Goldratt 在其著作 Critical Chain 中踢出来的项目管理方法,它侧重于项目执行所需要的资源,通过识别和管理项目关键链的方法来有效的监控项目工期,以及提高项目交付率。
  • AI Shell 让 AI 在命令行下提供 Shell 命令 AI Shell 是一款在命令行下的 AI 自动补全工具,当你想要实现一个功能,敲一大段命令又记不住的时候,使用自然语言让 AI 给你生成一个可执行的命令,然后确认之后执行。
  • 最棒的 Navidrome 音乐客户端 Sonixd(Feishin) Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。 Sonixd 是一款跨平台的音乐播放器,可以使用 [[Subsonic API]],兼容 Jellyfin,[[Navidrome]],Airsonic,Airsonic-Advanced,Gonic,Astiga 等等服务端。
  • 中心化加密货币交易所 Gate 注册以及认证 Gate.io 是一个中心化的加密货币交易所。Gate 中文通常被称为「芝麻开门」,Gate 创立于 2013 年,前身是比特儿,是一家致力于安全、稳定的数字货币交易所,支持超过 1600 种数字货币的交易,提供超过 2700 个交易对。
  • 不重启的情况下重新加载 rTorrent 配置文件 因为我在 Screen 下使用 rTorrent,最近经常调试修改 rtorrent.rc 配置文件,所以想要找一个方法可以在不重启 rTorrent 的情况重新加载配置文件,网上调查了一下之后发现原来挺简单的。