Linux 下查看内存使用

在 Linux 下,命令行就是一切,GUI 不是任何时候都可用的,所以掌握一定的常用命令,能够方便日常使用,比如查看进程,查看内存占用,等等,这篇文章就总结了一下 Linux 下查看当前系统占用内存的命令。

free

free 命令是最常用的查看系统使用内存及剩余可用内存的命令。free 命令能够显示系统使用和可用的物理内存,同时能够显示 swap 分区内容使用量。free 命令也能够显示被 kernel 使用的 buffers 和 caches。free 命令通过解析 /proc/meminfo 来收集信息。

free -m
              total        used        free      shared  buff/cache   available
Mem:          15911        8803        1100         886        6007        3750
Swap:         16246        3100       13146

-m 选项表示使用 MB 来显示数据,上面表示总共 15911 MB 内存,使用了 8803 M 内存。

A buffer is something that has yet to be “written” to disk. A cache is something that has been “read” from the disk and stored for later use.

上面的解释非常清晰, buffer 用于存放要输出到 disk 的数据,cache 是存放 disk 上读取的数据,这两者都为了提高 IO 性能由 OS 控制管理的。

还有一些其他比较有用的参数,比如 free -h 显示比较可读 h for human,还有 free -g 使用 GB 为单位显示信息。

/proc/meminfo

第二种方法就是直接读取 /proc/meminfo 文件, /proc 文件系统并不是真实的文件系统,不真正包含文件,他是虚拟文件系统,存储的是当前内核运行状态的一系列特殊文件。

运行命令 cat /proc/meminfo 检查其中的 MemTotal, MemFree, Buffers, Cached, SwapTotal, SwapFree 这几个字段,显示的内容和 free 命令是一样的。

cat /proc/meminfo
less /proc/meminfo
more/proc/meminfo
egrep --color 'Mem|Cache|Swap' /proc/meminfo

vmstat

通过 vmstat-s 选项来显示内存使用

vmstat -s
 16293800 K total memory
  9125168 K used memory
 10143256 K active memory
  2252796 K inactive memory
  1001072 K free memory
   746520 K buffer memory
  5421040 K swap cache
 16636924 K total swap
  3174096 K used swap
 13462828 K free swap
 33736485 non-nice user cpu ticks
    57413 nice user cpu ticks
 10985936 system cpu ticks
465486969 idle cpu ticks
  6378633 IO-wait cpu ticks
        0 IRQ cpu ticks
   114100 softirq cpu ticks
        0 stolen cpu ticks
 41745039 pages paged in
125109404 pages paged out
  1098642 pages swapped in
  2128871 pages swapped out
2199460184 interrupts
3279263642 CPU context switches
1514898192 boot time
   3676272 forks

前面几行就能看到内存使用。

vmstat 命令能够显示进程,内存,paging,block IO,traps,disks,和 cpu 的活动及使用状态。

top

top 命令通常被用来检查每一个进程的内存和 CPU 使用,但其实也可以用他来查看系统整个的内存使用,最上方的数据就能看到。

同样,可视化程度更高的 htop 也能够快速的查看到使用的内存

dmidecode

通过一下命令也可以查看,这个命令只能够查看到物理内存条的大小,但是可以提供给我们一个信息就是系统的总内存,是由两个 8G 内存组成,而不是由 8 个 2G 内存条组成。

sudo dmidecode -t 17

选项 -t--type 缩写

sudo dmidecode -t 17
# dmidecode 3.0
Getting SMBIOS data from sysfs.
SMBIOS 2.8 present.

Handle 0x001A, DMI type 17, 40 bytes
Memory Device
        Array Handle: 0x0019
        Error Information Handle: Not Provided
        Total Width: 64 bits
        Data Width: 64 bits
        Size: 8192 MB
        Form Factor: DIMM
        Set: None
        Locator: DIMM1
        Bank Locator: Not Specified
        Type: DDR4
        Type Detail: Synchronous
        Speed: 2400 MHz
        Manufacturer: 802C0000802C
        Serial Number: 12303315
        Asset Tag: 0F161300
        Part Number: 8ATF1G64AZ-2G3B1
        Rank: 1
        Configured Clock Speed: 2133 MHz
        Minimum Voltage: Unknown
        Maximum Voltage: Unknown
        Configured Voltage: 1.2 V

Handle 0x001B, DMI type 17, 40 bytes
Memory Device
        Array Handle: 0x0019
        Error Information Handle: Not Provided
        Total Width: 64 bits
        Data Width: 64 bits
        Size: 8192 MB
        Form Factor: DIMM
        Set: None
        Locator: DIMM2
        Bank Locator: Not Specified
        Type: DDR4
        Type Detail: Synchronous
        Speed: 2400 MHz
        Manufacturer: 802C0000802C
        Serial Number: 1230330E
        Asset Tag: 0F161300
        Part Number: 8ATF1G64AZ-2G3B1
        Rank: 1
        Configured Clock Speed: 2133 MHz
        Minimum Voltage: Unknown
        Maximum Voltage: Unknown
        Configured Voltage: 1.2 V

这个输出信息中可以看到电脑使用的两条 8G 的内存,类型 DDR4,速度是 2400 MHz.

下面这个命令也能够查看电脑上安装的内存条

sudo lshw -short -C memory
H/W path        Device     Class          Description
=====================================================
/0/0                       memory         64KiB BIOS
/0/18/15                   memory         128KiB L1 cache
/0/18/16                   memory         1MiB L2 cache
/0/18/17                   memory         8MiB L3 cache
/0/14                      memory         128KiB L1 cache
/0/19                      memory         16GiB System Memory
/0/19/0                    memory         8GiB DIMM Synchronous 2400 MHz (0.4 ns)
/0/19/1                    memory         8GiB DIMM Synchronous 2400 MHz (0.4 ns)
/0/19/2                    memory         [empty]
/0/19/3                    memory         [empty]
/0/100/1f.2                memory         Memory controller

GUI

同样可以使用 GUI 来查看,Gnome System Monitor 提供一个简单的界面显示系统进程,内存和文件系统。可以使用以下命令在终端开启

gnome-system-monitor

reference


2018-01-10 linux , memory , ram , free , command

Gson 使用笔记

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

基本使用

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();

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

斐讯 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 是一款企业级、高可用、可扩展的开源监控解决方案,提供实时报警、数据监控等功能。可以非常容易的监控整个服务器的状态,比如磁盘空间,端口存活,网络流量等等。

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

一些基础概念

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 个字段都是必须指定

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

https://lh3.googleusercontent.com/-b7vGrWO7M1Q/VeoaKTjWg_I/AAAAAAAA1uA/EKhtBNmcxzQ/s720-Ic42/IU%252520heart.jpg

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

电子书

最近文章

  • 使用命令行远程网络唤起主机 在 Linux 下可以通过 etherwake 命令来网络唤醒设备。
  • Cloud-init 初始化虚拟机配置 在安装 Proxmox 后在它的文档中了解到了 cloud-init。所以就来梳理一下。
  • BitTorrent 协议中的 BenCode 编码 在了解 BitTorrent 协议的时候,想着 .torrent 文件是如何生成的,所以就找了几个 CLI,比如 transmission-cli 和 mktorrent这两个开源的制作 torrent 文件的开源项目,发现他们就是按照一种约定的格式来生成文件。而这个约定的结构中就少不了现在要谈的 BenCode 编码。
  • Proxmox 设定直通硬盘 之前的文章讲了 Proxmox 的安装,以及在此基础上又安装了 OpenMediaVault,现在我的机器上一共三块硬盘,120 G SSD 安装了系统,并作为默认的 lvm,放一些 ISO,以及存放一些系统盘,另外的 1T 准备做 Proxmox 相关的数据盘,而剩下的一块 4T 盘想要直通给 OpenMediaVault 做数据盘。所以就产生了这样的一个需求。
  • OpenMediaVault 设置 OpenMediaVault,是一个开源的基于 Debian Linux 的下一代网络附加存储 (NAS) 解决方案。