威联通折腾篇十四:迁移系统盘

当时安装系统的时候就直接插入了一块硬盘,安装在了第一块机械硬盘上面,虽然读写也没有遇到什么瓶颈,但是记录以做备份,可以用于将系统迁移到 SSD 上。

下面的方法未经验证,慎用。应用可以迁移,但是一些配置可能无法成功备份到另一块硬盘中。

  1. Create a small new Volume using some of the unused space in Storage Pool 1
  2. Back up my System Setting (ControlPanel>Backup/Restore>Back up System Settings)
  3. Shut down NAS (systems that access the iSCSI storage will also be shut down)
  4. Pull the 1TB drive (System Volume)
  5. Put a 2 TB drive in both bay 3 and 4 (matching the model in bay 1 and 2)
  6. Power the NAS back on
  7. Restore from the backup taken in step 2 (point to the new volume created in step 1 for the System Volume)
  8. Add the new disks to the RAID Group in Storage Pool 1 and Migrate to RAID 5 then 6.

我把我的 NAS 系统盘搞砸了

2020 年 1 月 10 号更新。

qnap dashboard disk failed

我的 NAS 没有组 RAID,因为磁盘也不是一次买的,所以是一次次增加到 NAS 中的,每块磁盘都是独立的 Volumn,在这种情况下系统盘损坏也就无法从其他盘恢复了。

qnap storage snapshots

上面的方法在硬盘还能正常运行的时候可以试试,但是从昨天起发现 NAS 系统盘突然只读,而无法写入,经过一系列诊断初步感觉是硬盘故障了,所以赶紧备份系统盘数据,也幸亏 NAS 在要磁盘完全挂之前还保存了读的功能,所以赶紧 ssh 到后台 rsync 系统盘数据到其他盘。

qnap disk health check

备份完普通文件剩下的配置和应用就比较难备份了,前前后后花了很长时间去尝试和配置这些应用,但是最糟糕的可能就是要从头开始了。这么多应用和配置都要从头配置,想想就头疼。

qnap all applications

在网上经过一番调查,QNAP QTS 系统和数据是分开存储的,所以即使系统盘挂掉,其他磁盘不用其他操作,可以更换新的系统盘,然后安装新系统,然后在新系统中恢复原来的数据。

先在”存储与快照总管”,安全卸载存储区。所有的盘都卸载掉,然后关机。换一个用来安装系统和应用的硬盘,重装系统。装好之后插上旧数据硬盘。在存储与快照总管那里,扫描闲置磁盘。然后原来的数据就恢复了。

安全卸载磁盘:

  • 主菜单,存储与快照总管,存储,存储 / 快照
  • 选择存储池
  • 管理,打开存储池管理窗口
  • 删除 > 安全卸载存储池 (注意这里千万要选择卸载存储池,而不是移除存储池)
  • 单击“是”,存储池状态更改为安全卸载中…。QTS 卸载完存储池后,将从存储与快照总管中消失。
  • 从 NAS 中移除包含存储池的硬盘

恢复磁盘:

  • 在第二个 NAS 上安装硬盘。
  • 在第二个 NAS 上,转到主菜单 > 存储与快照总管 > 存储 > 磁盘 /VJBOD
  • 选择恢复 > 扫描闲置磁盘。 此时会出现确认消息。 单击确定。
  • QTS 将扫描磁盘并检测存储池。
  • 单击应用。

该方法也适用于将存储池移动到其他 NAS 中。1

storage pool

对于我这种只用了一块磁盘一个静态卷来安装系统的,恢复起来就比较麻烦了,大思路就是:

  • 通过 QTS 自带的备份来备份系统设置
  • 备份该盘中所有数据
  • 备份 /share/CACHEDEV1_DATA/.qpkg/ 目录下应用数据及配置
  • 再将其他数据磁盘数据卷通过上面的方法移除,然后关机,再将系统磁盘移除
  • 然后插入新的磁盘,在新磁盘上新建卷,安装系统
  • 在新系统中恢复之前备份的设置
  • 重新安装应用,然后从备份中恢复之前备份的应用数据

reference


2019-07-18 qnap , backup , system , nas

Intellij IDEA 支持 jQuery

在 设置中进行如下操作开启 jQuery 支持:

  • First press CTRL + ALT + S and go to settings.
  • Then click from the menu Languages & Frameworks
  • Select Javascript from the section below and select Libraries
  • In the open menu on the right, click on the Download button and select JQuery from the list
  • Download it and apply. It is done.

reference


2019-07-14 intellij , intellij-idea , javascript , jquery

使用 Tampermonkey 调试本地脚本

记录一下使用 Tampermonkey 调试本地脚本。

Tampermonkey 加载开发调试本地 js

首先设置 Tampermonkey 插件的设置

  • Chrome 中打开 chrome://extensions
  • 搜索 Tampermonkey ,并且在设置中开启 Allow access to file URLs

然后在文件中使用 @require 引入外部文件。

// ==UserScript==
// @name         Debug Userscript
// @namespace    https://github.com/einverne/userscripts
// @version      0.1
// @description  This is a debug script to load userscripts from local file system. NOTICE, you need to turn on Allow access to file URLs to @require local file https://www.tampermonkey.net/documentation.php
// @author       Ein Verne
// @match        http*://*
// @include      http://*
// @include      https://*
// @include      *
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_getResourceText
// @require      https://unpkg.com/dexie@latest/dist/dexie.js
// @require      file:///home/einverne/Git/userscripts/douban_export/douban_exporter.user.js
// ==/UserScript==

(function () {
    'use strict';

    console.log("debug script start here");
    // Your code here...
})();

reference


2019-07-13 tampermonkey , userscripts , userscript

Linux 下设置编码格式 locales

很多人在程序中会处理 non-ASCII 字符,在日志中,在终端显示中等等。

locales installs

检查 locales 是否安装

dpkg -l locales

如果 locales 之前显示 ii 表示已经安装了,否则

sudo apt install locales

重新配置

dpkg-reconfigure locales

locales 配置

使用命令 locale 查看配置

locale
LANG=en_US.UTF-8
LANGUAGE=en_US:en
LC_CTYPE=en_US.UTF-8
LC_NUMERIC=zh_CN.UTF-8
LC_TIME=zh_CN.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=zh_CN.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=zh_CN.UTF-8
LC_NAME=zh_CN.UTF-8
LC_ADDRESS=zh_CN.UTF-8
LC_TELEPHONE=zh_CN.UTF-8
LC_MEASUREMENT=zh_CN.UTF-8
LC_IDENTIFICATION=zh_CN.UTF-8
LC_ALL=

.bashrc 中放入如下设置:

export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

Q & A

某些情况下在终端 less /path/to/logfile 会显示 \u 开头的中文编码,这是 unicode 编码,大部分情况下 less 会使用 UTF-8 去查看文件。但是如果文件编码格式非 UTF-8 那么可能会有些问题。

使用 file /path/to/file 来查看文件的编码。

reference


2019-07-12 locale , unix , linux , encoding , utf8

jbpm 流程

BPMN 中可执行的工作流包含一系列不同类型的节点,这些节点可以被用来连接生成有序的工作流。BPMN 2.0 规范定义了三种主要的类型:

  • Events, 用来定义特定事件的发生。可以是起始事件(用来表示工作流的开始),结束事件(用来表示工作流的结束,或者子流程结束),中间事件(表示发生在工作流执行过程中的事件)
  • Avtivities, 定义了在工作流执行过程中需要执行的不同动作。依据不同类型的内容,存在不同类型的 tasks,并且 activities 可以被嵌套
  • Gateways, 用来定义不同路径,根据不同类型的 Gateway,这可能是 parallel.

jBPM6 并没有实现 BPMN 2.0 规范中的所有元素和属性,但是提供了大部分重要的子集。

节点

Events

  • Start Event (None, Conditional, Signal, Message, Timer)
  • End Event (None, Terminate, Error, Escalation, Signal, Message, Compensation)
  • Intermediate Catch Event (Signal, Timer, Conditional, Message)
  • Intermediate Throw Event (None, Signal, Escalation, Message, Compensation)
  • Non-interrupting Boundary Event (Escalation, Signal, Timer, Conditional, Message)
  • Interrupting Boundary Event (Escalation, Error, Signal, Timer, Conditional, Message, Compensation)

Activities

  • Script Task
  • Task
  • Service Task
  • User Task
  • Business Rule Task
  • Manual Task
  • Send Task
  • Receive Task
  • Reusable Sub-Process (Call Activity)
  • Embedded Sub-Process
  • Event Sub-Process
  • Ad-Hoc Sub-Process
  • Data-Object

Gateways

  • Exclusive
  • Inclusive
  • Parallel
  • Event-Based

本文主要内容总结自 jboss jbpm 官方文档的第 8 章内容。

reference


2019-07-12 jbpm , business-process-model

Linux Mint 连接 802.1x EAP wifi network

Linux Mint 在连接 802.1x EAP 网络时,一直无法弹出用户名密码弹窗,导致一直无法连接这些网络。今天查了一下,需要手动进行连接。

打开 Network Manager,选择 Connect to Hidden Network.

  • Wi-Fi security 中选择 WAP & WPA2 Enterprise
  • 在弹出的复杂的对话框中
  • Authentication 选择 Protected EAP(PEAP)
  • 然后输入 Username 和 Password ,证书可选

选择连接即可。

reference


2019-07-11 linux-mint , wifi , wifi-network , eap , wifi-authentication

一键去除网易云音乐广告

前提条件

  • Android 手机
  • root 权限
  • Root Explorer

打开目录

/data/media/0/netease/cloudmusic/

看到 AD 目录,去除写入权限即可。

Root Explorer 如果打开的是 /sdcard 下面的目录可能无法设置权限。

netease ads remove


2019-07-09 netease , music , ads

Spring 自定义 namespace and handlers

自定义 namespaces 可以让用户有一种更方便的方式来定义 Bean。

Spring 提供了一些开箱及用的方式,比如 <mvc:annotation-driven/> 可以参考这篇文章 来查看该配置的作用。

Spring 从 2.0 开始可以支持自定义扩展 XML Schema。

XML Schema-based configuration

在了解自定义 XML Schema 之前首先要熟悉一下 Spring 的 XML Schema 配置。最简单的配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- bean definitions here -->

</beans>

如果要引入 util schema 需要这样修改

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- bean definitions here -->

</beans>

扩展 XML

实现自己的 XML

  • 创建 XML Schema 文件 xsd
  • 自定义处理器,实现 NamespaceHandler 接口
  • 自定义解析器,实现 BeanDefinitionParser 接口,可多个
  • 注册到 Spring 容器中

官方文档举了一个简单的例子,比如想要在 context 中定义

<myns:dateformat id="dateFormat"
    pattern="yyyy-MM-dd HH:mm"
    lenient="true"/>

这样的代码,那么需要做下面一些事情。

定义 XML

定义如下 dateformat.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.mycompany.com/schema/myns"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns:beans="http://www.springframework.org/schema/beans"
        targetNamespace="http://www.mycompany.com/schema/myns"
        elementFormDefault="qualified"
        attributeFormDefault="unqualified">

    <xsd:import namespace="http://www.springframework.org/schema/beans"/>

    <xsd:element name="dateformat">
        <xsd:complexType>
            <xsd:complexContent>
                <xsd:extension base="beans:identifiedType">
                    <xsd:attribute name="lenient" type="xsd:boolean"/>
                    <xsd:attribute name="pattern" type="xsd:string" use="required"/>
                </xsd:extension>
            </xsd:complexContent>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

编写 NamespaceHandler

编写 NamespaceHandler 来处理特定 namespace 下的元素。NamespaceHandler 在这个例子中应该处理好 myns:dateformat 的解析工作。

NamespaceHandler 接口非常简单,有三个方法:

  • init() 初始化 NamespaceHandler
  • BeanDefinition parse(Element, ParserContext) 会被 Spring 在顶层元素处理时调用
  • BeanDefinitionHolder decorate(Node, BeanDefinitionHolder, ParserContext) 处理属性或者嵌套元素时使用

比如:

import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

public class MyNamespaceHandler extends NamespaceHandlerSupport {

    public void init() {
        registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser());
    }

}

编写 BeanDefinitionParser

BeanDefinitionParser 会被 NamespaceHandler 内部使用,当解析特定的元素时会对应不同的解析器。比如这个例子中 dateformat 使用了 SimpleDateFormatBeanDefinitionParser 。

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

import java.text.SimpleDateFormat;

public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { 1

    protected Class getBeanClass(Element element) {
        return SimpleDateFormat.class; 2
    }

    protected void doParse(Element element, BeanDefinitionBuilder bean) {
        // this will never be null since the schema explicitly requires that a value be supplied
        String pattern = element.getAttribute("pattern");
        bean.addConstructorArg(pattern);

        // this however is an optional property
        String lenient = element.getAttribute("lenient");
        if (StringUtils.hasText(lenient)) {
            bean.addPropertyValue("lenient", Boolean.valueOf(lenient));
        }
    }
}

Registering the handler and the schema

所有的编程都已经结束,剩下来的就是如何让 Spring XML 感知到所做的修改,将自定义内容注册到 Spring 中。

要实现这一点,需要考虑两点

  • 注册自定义的 NamespaceHandler
  • 注册 XSD 文件

在 resources 下创建 META-INF 目录,并创建如下两个文件

  • spring.handlers 包含对应的 XML Schema URI 到 Handler 类的映射
  • spring.schemas 包含 xsd 文件路径

META-INF/spring.handlers

定义 XML Schema 到 Handler 类映射,这个例子中

http\://www.mycompany.com/schema/myns=org.springframework.samples.xml.MyNamespaceHandler

META-INF/spring.schemas

定义 XML Schema 到自定义 XSD 文件映射

http\://www.mycompany.com/schema/myns/myns.xsd=org/springframework/samples/xml/myns.xsd

当做完这些后,那么上面定义的内容就和如下的定义可以实现完全相同的功能。

<bean id="dateFormat" class="java.text.SimpleDateFormat">
    <constructor-arg value="yyyy-HH-dd HH:mm"/>
    <property name="lenient" value="true"/>
</bean>

使用自定义 XML Schema

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:myns="http://www.mycompany.com/schema/myns"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.mycompany.com/schema/myns http://www.mycompany.com/schema/myns/myns.xsd">

    <!-- as a top-level bean -->
    <myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true"/>

    <bean id="jobDetailTemplate" abstract="true">
        <property name="dateFormat">
            <!-- as an inner bean -->
            <myns:dateformat pattern="HH:mm MM-dd-yyyy"/>
        </property>
    </bean>

</beans>

代码见 https://github.com/einverne/thrift-swift-demo/tree/master/spring-mvc-demo

reference


2019-07-05 spring , java , java-web , spring-mvc

shell script idiom

Bash 命令中一些常见的习惯。

> file redirects stdout to file
1> file redirects **stdout** to file
2> file redirects **stderr** to file
&> file redirects stdout and stderr to file

/dev/null is the null device it takes any input you want and throws it away. It can be used to suppress any output.

Using 2>&1 will redirect stderr to whatever value is set to stdout (and 1>&2 will do the opposite).


2019-07-03 bash , shell , stdout , stderr , pipeline

MySQL 日期和时间函数

记住一些常用的时间操作函数能够提高 SQL 查询的效率。比如查询过去 30 天的记录,如果不清楚 DATE_SUB() 函数可能需要手动计算一下时间点再查询,但是如果知道 DATE_SUB() 函数就可以

SELECT something FROM tb_name WHERE DATE_SUB(CURDATE(), INTERVAL 30 DAY) <= daet_col;

这个 SQL 同样会查找出来当前时间点未来的记录。

获取时间

获取当前时间

mysql> SELECT NOW();
+---------------------+
| NOW()               |
+---------------------+
| 2019-07-01 09:12:46 |
+---------------------+
1 row in set (0.00 sec)

获取时间戳,同义于 NOW()

mysql> SELECT CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP;
+---------------------+---------------------+
| CURRENT_TIMESTAMP() | CURRENT_TIMESTAMP   |
+---------------------+---------------------+
| 2019-07-01 09:13:13 | 2019-07-01 09:13:13 |
+---------------------+---------------------+
1 row in set (0.00 sec)

获取 UNIX 时间戳

mysql> select UNIX_TIMESTAMP(NOW());
+-----------------------+
| UNIX_TIMESTAMP(NOW()) |
+-----------------------+
|            1562571055 |
+-----------------------+
1 row in set (0.00 sec)

获取当前的时间 CURTIME(),结果为 hh:mm:ss 格式

mysql> SELECT CURTIME();
+-----------+
| CURTIME() |
+-----------+
| 09:13:53  |
+-----------+
1 row in set (0.01 sec)

或者

mysql> SELECT CURTIME() + 0;
+---------------+
| CURTIME() + 0 |
+---------------+
|         91525 |
+---------------+
1 row in set (0.02 sec)

计算时间

如果能查得当前时间,那么通过计算函数就能够非常快速的得到,比如过去一周,过去三十天的时间戳。

时间转换

从一个时间转换成另外一种表现方式

  • 天数 <==> 日期
  • UNIX 时间戳 <==> 日期
  • 秒数 <==> 时间

TO_DAYS

传入日期,返回一个从第 0 年 ( ‘0000-00-00’) 开始到传入日期的天数

mysql> SELECT TO_DAYS('0000-01-01');
+-----------------------+
| to_days('0000-01-01') |
+-----------------------+
|                     1 |
+-----------------------+

同理还有 TO_SECONDS(expr) 方法

FROM_DAYS

给定一个天数,返回日期。

相类似的方法还有 FROM_UNIXTIME(unix_timstamp),接受一个时间戳返回日期

时间到秒

TIME_TO_SEC(time) 传入时间返回秒数

mysql> SELECT TIME_TO_SEC('22:23:00');
        -> 80580
mysql> SELECT TIME_TO_SEC('00:39:38');
        -> 2378

同理从秒数到时间 SEC_TO_TIME(seconds)

时间日期计算

给日期增加或者减少,加减运算

时间间隔

增加时间

ADDDATE(date,INTERVAL expr unit), ADDDATE(expr,days)
DATE_ADD(date,INTERVAL expr unit), DATE_SUB(date,INTERVAL expr unit)

举例

mysql> select DATE_ADD(NOW(), INTERVAL 1 DAY);
mysql> select DATE_ADD(NOW(), INTERVAL 1 HOUR);
mysql> select DATE_ADD(NOW(), INTERVAL 1 MINUTE);
mysql> select DATE_ADD(NOW(), INTERVAL 1 SECOND);
mysql> select DATE_ADD(NOW(), INTERVAL 1 MICROSECOND);
mysql> select DATE_ADD(NOW(), INTERVAL 1 WEEK);
mysql> select DATE_ADD(NOW(), INTERVAL 1 MONTH);
mysql> select DATE_ADD(NOW(), INTERVAL 1 QUARTER);
mysql> select DATE_ADD(NOW(), INTERVAL 1 YEAR);
mysql> select DATE_ADD(NOW(), INTERVAL -1 YEAR);

同理减时间

SUBDATE(date,INTERVAL expr unit), SUBDATE(expr,days)
DATE_SUB(date,INTERVAL expr unit)

reference


2019-06-30 mysql , database , sql , date , time

电子书

最近文章

  • 威联通折腾篇十七:Docker 安装的 NextCloud 升级、备份及恢复 之前有文章写过如何在 Qnap 上使用 Container Station 来安装 NextCloud,之前重度使用 NextCloud,里面已经存了近 70G 的文件内容,这次系统重建后,下载新的镜像,然后重新恢复,费了一番时间,主要是恢复数据库,然后还有本地挂载的文件,以及升级版本。
  • 威联通折腾篇十六:为 Container Station 更换镜像 都知道其实 QNAP 的 Container Station 就是 Docker,所以桌面版可以修改的国内镜像地址,QNAP 系统上也能够修改,可以快速提高镜像的下载速度。
  • 备份数据及系统思考 上周五 NAS 系统盘挂掉后一个周末都没有过好,一边忙着备份数据,一边要忙着整理系统应用和配置。早以前除了云端同步数据曾经出现过一两次数据丢失的情况,本地保存的数据还没有出现过管理的问题,系统会用 Clonezilla 全量备份,笔电数据则辅以同步工具 Dropbox 和自建的 NextCloud,平时丢数据的可能倒是比较小,但唯一疏漏的 NAS,因为硬盘不是一次性买全而是分了几次,所以从一开始就没有规划好存储与备份,而系统的酷狼 4T 盘可能经过几次家里停电,SMART INFO 出现警告的时候也没有来得及备份,所以造成了从周五开始突然系统盘只读状态,无奈只能立即开启 rsync 手动先将系统盘中的数据备份到其他盘。等到周日把损坏的系统盘送修之后,是时候来思考一下如何管理本地数据了。
  • QNAP 上 SQL server 数据备份 平时没有注意备份 QNAP 上系统盘的数据,从昨天开始系统盘突然只读,而无法写入,发现磁盘有问题了,无奈只能边申请售后,边想着怎么备份数据,还要恢复这么多的配置。
  • 云服务 free tier AWS