Apache Guacamole – All in WEB

1. Ubuntu 20.04 下安装 Docker,添加当前用户到 docker 组
curl -fsSL https://get.docker.com | bash -s docker –mirror Aliyun
sudo usermod -aG docker $USER

2. 拉取镜像
docker pull guacamole/guacamole
docker pull guacamole/guacd
docker pull mysql/mysql-server:5.7

3. 导出 guacamole 中的数据库初始脚本,启动 MySQL 数据库
docker run –rm guacamole/guacamole /opt/guacamole/bin/initdb.sh –mysql > initdb.sql
docker run –name mysql –restart=always -e MYSQL_ROOT_PASSWORD=123456 -d mysql/mysql-server:5.7

4. 复制到容器中,进入容器 MySQL Client
docker cp initdb.sql mysql:/initdb.sql
docker exec -it mysql mysql -uroot -p123456

5. 创建数据库、用户密码,导入初始脚本
CREATE DATABASE guacamole;
CREATE USER ‘guacamole’@’%’ IDENTIFIED BY ‘guacamole’;
GRANT SELECT,INSERT,UPDATE,DELETE ON guacamole.* TO ‘guacamole’@’%’;
FLUSH PRIVILEGES;
USE guacamole;
SOURCE initdb.sql;

6. 启动 guacamole
docker run –name guacd –restart=always -d guacamole/guacd
docker run –name guacamole –restart=always –link guacd:guacd –link mysql:mysql -e MYSQL_DATABASE=’guacamole’ -e MYSQL_USER=’guacamole’ -e MYSQL_PASSWORD=’guacamole’ -d -p 8080:8080 guacamole/guacamole

浏览器打开 http://localhost:8080/guacamole 默认用户名密码 guacadmin。配置好SSH,RDP,VNC,主机可以是内网中任何一台能访问的机器,RDP 注意关闭认证和选上网络级别身份验证(NLA)。配合御花园(https://ifport.com)将 8080 端口映射到公网,随时在浏览器中访问(注意安全)。

自此 All in WEB,Docker 大法好。

阿里云日志服务接入小计

上个月为了消灭阿里云后台的一个 Linux 系统漏洞告警,对服务器执行了一次 apt-get upgrade,顺便重启了一下。在恢复各种后端服务的时候,忘记启动 Solr Server,导致歌词查询服务一直不可用。当时也恰好没有对 Solr 做可用性监测,就这么挂了整整4天,手动启动后恢复正常。复盘后发现其实日志已经报的很明确了,只是没有监控到位,于是实时日志监控必须要安排上了。

早期项目上线时候也写过一些脚本来搜索日志中的特定关键字,比如“ERROR”,“EXCEPTION”等,但都不太完善,维护也困难,没太当回事。实际工作中,我们一般采用 ELK 三剑客 + 企业微信或者钉钉群推送来做通知。这一套搭建起来成本并不低,甚至会高于项目本身。

对比了一些第三方后,决定还是用阿里云 SLS 日志服务,主要是免费额度够用,结合之前的邮件、短信告警,接入还是比较简单,一步一步按向导走即可,这里有个小插曲,第一次按向导创建了一个 Logstore 后,在删除上级关联后成了游离 Logstore,无法再使用,工单后只能用 API 删除。

一套优秀的日志服务,无外乎都有以下几大块。

1. 数据采集

阿里云数据采集支持的数据源非常多,也有相对应的 SDK 提供,这里主要采用安装 Logtail Agent 来读取 Java logback 日志进行采集,对程序透明,无需停机,实时采集。阿里云的 Logtail 提供了管理后台来配置读取路径,解析格式等,非常方便,无需写配置文件。

2 ECS 安装 Logtail

对于已经使用了阿里 ECS 的主机,在配置向导中可以自己选择实例自动安装。

3 后台设置 Project、Logstore、Logtail

一个 Project 可对应多个 Logstore 日志存储,即对应多个项目,或者多个服务模块,可关联多个 Logtail 采集器,适合集群环境。

Project 是一个方便管理日志而定义的,例如我按地域节点划分了三个 Project,(microservice-log)深圳节点,(microservice-log-hk)香港节点,(bill-analysis)账单。Project 有地域限制,即同一个地域的机器才能放到同一个 Project 中。

Logstore 为实际的日志存储,采集的日志都存储在这里。默认会自己创建一个,按量付费,酌情使用 shard 和保存时间,避免超出免费额度。

Logtail 为采集程序,创建好 Logstore 会引导配置 Logtail,主要是配置下采集日志的路径和格式,这里我简单列举下正在使用的配置。

日志路径我配置为:/usr/local/applications/lyric8280/logs/∗∗/lyric.log

我之前设置的 SpringBoot 的日志在 jar 包目录的 logs 下,并按天产生一个新文件,旧文件打包,并在30天后删除,因此采集器配置就指向上述路径即可。“/∗∗/”可以表示任意目录,如果你的日志路径中有动态部分,可以放到这里。

设置采集模式:完全正则模式,由于日志中有换行和存在 Java stack 日志,需要关闭单行模式。

日志示例:2020-10-15 00:00:29.049 INFO 1672 — [Thread-5038] com.dorole.service.SolrSearchService : <手放开 李圣杰> Solr 总数:3

设置首行正则表达式:\d+-\d+-\d+\s\d+:\d+:\d+\.\d+\s.*

验证一下,成功匹配数:7,说明符合要求,这时候的日志还不是结构化的数据,勾选提取字段。

设置正则:(\d+-\d+-\d+\s\d+:\d+:\d+.\d+)\s+(\S+)\s(\d+)\s(\S+)\s(\[.*\])\s(\S+)\s+:\s(.*)

验证一下,就可以得到key,value了,对key进行命名后,就完成了结构化日志。

取消使用系统时间,设置时间转换格式:%Y-%m-%d %H:%M:%S

验证通过后,就完成了一个Logtail配置。这里推荐使用 https://regex101.com/ 在线debug正则。

4. 日志查询

日志查询还是比较习惯终端登录,less/tail查询,阿里的这个查询就我基本忽略了,主要是为了配合后面的监控,有兴趣的可以深入研究SQL查询语法,号称5条件内秒查10亿级数据。

5. 告警设置

告警其实就是一组特殊的查询,以时间范围内查出指定内容,执行某个任务。

例如,设置查询语句:level = ERROR | select logger, msg,时间区间一分钟(相对),频率间隔:1分钟,触发条件:$0.logger != ”。

即“ERROR”级别的日志一分钟内出现一次以上,则触发后续通知动作。通知按需设置即可,可携带变量,默认也够用了。

到此,基本就结束了,别忘记多测试下,确保没有配置错误。看似简单的需求,背后实现还是挺复杂的。

噢,对了,开通日志服务后有免费的账单分析服务,会新起一个 Project ,每日推送账单,还挺方便的,各种费用清清楚楚。

更多参考官方文档:https://help.aliyun.com/product/28958.html

Java 基于权重按比例分配算法

public class WeightRandomStrategy<K, V extends Number> {
    private TreeMap<Double, K> weightMap = new TreeMap<>();

    public WeightRandomStrategy(List<Pair<K, V>> list) {
        for (Pair<K, V> pair : list) {
            double lastWeight = this.weightMap.size() == 0 ? 0 : this.weightMap.lastKey();
            this.weightMap.put(pair.getValue().doubleValue() + lastWeight, pair.getKey());
        }
    }

    public K random() {
        double randomWeight = this.weightMap.lastKey() * Math.random();
        SortedMap<Double, K> tailMap = this.weightMap.tailMap(randomWeight, false);
        return this.weightMap.get(tailMap.firstKey());
    }
}
List<Pair<String, Integer>> list = new ArrayList<>();
list.add(new ImmutablePair<>("TR", 90));
list.add(new ImmutablePair<>("TX", 10));
WeightRandomStrategy<String, Integer> strategy = new WeightRandomStrategy<>(list);
int a = 0, b = 0;
for (int i = 0; i < 10000; i++) {
    switch (strategy.random()) {
        case "TR":
            a++;
            break;
        case "TX":
            b++;
            break;
        default:
            break;
    }
}
System.out.println("a=" + a + ", b=" + b);
System.out.println("a+b=" + (a + b));

---------------------------------------------------output
a=8993, b=1007
a+b=10000

ThreeTen Extra 时间日期处理利器

  最近一些需求有对多个时间区间进行判断,例如交集之类的,而2个时间区间可以多达13种情况,实现起来特别容易绕晕,正好找到这样一个工具类可以满足需求,只需要一个方法便可计算出结果,很方便。ThreeTen 的设计里面 Instant 表示时间点,Interval 表示时间段,使用Interval即可对区间进行判断。

  例如:判断是否有交集(Overlaps)

Instant startA = Instant.parse("2018-08-01T00:00:00Z");
Instant stopA = Instant.parse("2018-08-10T00:00:00Z");
Instant startB = Instant.parse("2018-07-30T00:00:00Z");
Instant stopB = Instant.parse("2018-08-02T00:00:00Z");

Interval areaA = Interval.of(startA, stopA);
Interval areaB = Interval.of(startB, stopB);
boolean flag1 = areaA.overlaps(areaB);

  同样的,还有是否邻接、包含、相等、之前,之后等等。当然,除了Interval,还有别的类可以用,非常强大。官方文档也非常详细。

  官网链接:https://www.threeten.org/threeten-extra/index.html

  Maven

<dependency>
    <groupId>org.threeten</groupId>
    <artifactId>threeten-extra</artifactId>
    <version>1.4</version>
</dependency>

Podam 一个Pojo填充随机值利器

  开发中常常遇到需要Mock数据来测试功能是否完整,手动一个一个填数据有点浪费时间,最近找到这样一个小工具,非常方便的给Pojo类所有字段填上随机值,非常好用,这里推荐下。

官网:https://mtedone.github.io/podam/

使用方法:

1、添加Maven依赖

<dependency>
    <groupId>uk.co.jemos.podam</groupId>
    <artifactId>podam</artifactId>
    <version>7.1.1.RELEASE</version>
</dependency>

2、基础使用

public class Case {
    private Long id;
    private Long applyId;
    private Long billId;
    // get/set ......
}

PodamFactory factory = new PodamFactoryImpl();
Case case = factory.manufacturePojo(Case.class);

  就这样简单的调用,case这个对象的三个字段都是随机值了,基本上满足测试需求,当然Podam远不止这么简单。