索尼zx300a与森海ie80s

作为一个非重度音频发烧友,入手 zx300a 与 ie80s 已经是一个月前的事了,直接的推动因素是手机内存空间不足了,可怜的 iPhone XS Max 64G,间接因素是种草已多年,一直没下手,总觉得手机不更方便吗?

日常使用网易云音乐,离线的曲目占了 10G 左右的空间,似乎看起来换个大容量手机就能解决的问题,直到 iPhone 12 系列面世,左右摇摆了下,最终放弃了这个想法。

其一,XS Max 还没到替换周期,其二手机挂小尾巴 DAC 的方式终究是个不稳定的方案,爆音,延迟,耗电等小毛病多,官方的 3.5mm Jack 又有点嫌弃,其三,也是最无解的,各种 App 静音后台、视频自动播放等独占音频资源操作,导致听歌要被无数次打断,恶心至极。其四,中国联通的网络质量,大家都懂的。

于是下决心将听音独立出来,这就有了 zx300a,对比过新出的 zx505,对后者糟糕续航实在不满意,果断放弃。zx300a 是个纯粹的听歌平台,没有花哨功能,每一个功能就是极其实用。

首先就是双向蓝牙功能,这个在实际使用者非常明显,可以将 iPhone 的输出通过蓝牙转到 zx300a 上进行播放,在通过 zx300a 的有线或蓝牙播放出来,相当于一个中继。偶尔想看个视频,也不用担心没带 3.5mm Jack。

zx300a 的音乐管理方式就是普通的文件夹管理,不用安装特别软件,沿用 PC 端目录结构。相比往 iPhone 上传歌,往往要借助一个 Web Upload 界面。

网易云音乐下载的歌曲可以很方便的转换成 flac 或 mp3,复制到 zx300a 上就可以播放,不用担心会员到期,或者歌曲下架无法再听了,即便想听榜单新歌或者播客之类的,直接蓝牙 streaming 过来,超级神器。并非要像 zx505 一样弄个安卓系统,再装一个网易云音乐。

支持 USB DAC,高规格的外置声卡,虽然没有经常这么用,有胜于无。

要说缺点,可能就是这个 WM-PORT 专用数据线,在这个 Type-C 到处飞的年代,有点太过时了,外出时间长,还是得多带根数据线。

再说说森海塞尔 ie80s,在此之前主力一直是 Bose QC 30,也算是地铁通勤神器,降噪效果非常好,佩戴也舒服,可到了冬季,衣服厚一点佩戴就不那么友好了。

ie80s 是为数不多的可以不用挂耳佩戴,眼镜党的福音,又以音质著称的 Hifi 入门级耳机,双11特价便入手了,虽然海外版更便宜,当时没注意。

4.4mm 平衡线,更换平衡线主要有三大考虑:一是原厂线很容易打圈圈,不好看,二是 zx300a 耳机口是在顶部,放入口袋,直插头容易压弯,三是播放器的平衡端推力更大,满足 ie80s 的要求,顺便点亮 BALANCE 标,至于音质改善多少,除了能感受立体感清晰了点,声音大了点,其他“玄学”就因人而异了。

高采样 Hi-Res 之后,或许不再需要去调 EQ 了,如同拍照,有了 HDR 之后,或许不在需要一级调色,因为优质的器材终究是更好的还原现场。配了一张 256G 的 TF 卡,应该满足未来几年使用了。

阿里云日志服务接入小计

上个月为了消灭阿里云后台的一个 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

公网IP vs 内网穿透

最近在开发「即刻大陆」公众号的时候,由于微信的一些限制,在本地测试公众号需要配合一个公网地址,这就导致了没有公网IP的环境下出现许多不必要的麻烦。当然这对一个技术从业者并不是什么难事,早在去年就开发了「御花园」这个项目来做内网穿透,这不又用上了。顺便好好来聊聊这个话题。

研发「御花园」的最初目的是因为家里添置了一台Windows Server,当做NAS使用,需要RDP远程控制,带宽要求较高,折腾过各种开源的方案,均不太理想,主要有以下方案。

Ngorkhttps://ngrok.com/ ):官方中转服务器在国外,慢就不说了,国内很多收费的节点,服务能力各有千秋。其早期版本已经开源出来,可以自己搭建,采用Go语言编写,端口配置依赖客户端配置,仅有简单的权限控制,长期使用并不太方便,也不太安全。

FRPhttps://github.com/fatedier/frp ):和Ngork类似,也是需要服务器中转,提供的配置更为丰富,不过官方一直声明不建议用在生产环境,便没有过多的研究,支持Unix 套接字和DNS转发,实验性的点对点内网穿透,甚至还能搭建成一个https服务来转发非https流量,也算是一大特色。

ZeroTierhttps://www.zerotier.com/ ):和上述基于传输层协议不同,这个是工作在二层协议,可以提供多个设备组建虚拟局域网进行互通,大部分配置在官网后台操作,设备上只需要安装一个客户端,Join一个网络,激活即可,相当方便。可以自建卫星节点来加速。虚拟LAN兼容性较好,但网络不理想的情况下,上层不容易感知。

花生壳https://hsk.oray.com/ ):作为Oray旗下的老牌服务,从最初的DDNS转型到内网穿透,可以说是很顽强了,免费用户有1M的带宽,一个端口可用。他们家的Windows客户端还是做的比较稳定。

工慾善其事,必先利其器,综上所述,一个工具好用,清晰便捷的图形化操作必不可少,客户端则尽可能的透明,稳定。出于职业特性,对于TCP传输层的操作,Java Netty性能毋庸置疑,且任何需求只要有一个人需要,就有成千上万人需要。「御花园」便孕育而生( https://ifport.com/ ),参考了众多的开源实现,封装了一个私有IPF报文,SSL加密数据,同时写了一个管理后台,方便的自行注册/添加/删除接入设备和端口映射,端口随机自动分配,客户端实时刷新生效,完全无须人工介入。在19款macbookpro上部署,采用iperf3测试local转发能力在1.6Gb/s 左右,受制cpu/内存限制,成绩应该还能更高。

公网IP之争,在19年下半年开始,国家大力推广IPv6,似乎IPv4也没那么紧缺,运营商打打电话,工信部走一波,基本也能得到,还是v4,v6双公网(顺便升级了博客v6支持),路由器绑定DDNS,远程控制更加便捷,内网穿透似乎没有必要了。

随着生活的变迁,环境的变化,常在租住地,咖啡馆WiFi,新家4G之间的来回切换,公网IP并非万能,抗网络波动更是重要,众所周知的原因,运营商PPPoE存在强制断线的坑,DDNS生效有一定的延迟。「御花园」又有了用武之地 ,同一个端口,穿越任意网络,短周期心跳检测、重连,抗网络波动极为灵活,客户端几乎无感知,不得不说还是自研的用着顺手,但迫于带宽和成本压力,也没计划推广。

「御花园」和「即刻大陆」并非什么高深的技术,不过是在这个特定时期能让你看的更远。

Mavic 2 带屏遥控器

去年在云南的一次自驾游,用的是普通版遥控,每次停车,要退出导航,停掉音乐,拔下手机,接上遥控器,打开DJI GO,拍完之后又得拔下来,插到车上继续导航,继续音乐,非常之麻烦,一直期待也能像“悟”系列一样有个屏幕才好,摆脱手机的依赖。直到DJI发布了新的带屏遥控器,观望许久后决定购入,DJI的东西一定不要第一时间购买,BUG会多的让你疯狂。

带屏遥控器到手后,一块5.5寸的屏幕确实让人惊艳,拥有和iPhone Retina屏一样细腻,只是玻璃黑框比较宽了点,在这个全面屏时代,显得很没设计感。高亮模式在太阳下也能看的清楚。由于体积的增大,遥控器握持手感比之前好了很多,重量也重了许多,在户外,一手拿遥控器,一手拿飞机,关车门都得靠肘。细心的大疆还在遥控器底部留了两个螺丝孔,却又不送个肩带,只能在万能的淘宝上购了一条肩带。由此想到应该要有一个遥控器锁定的物理键,以方便挂着走动,腾出双手。

自然有了屏幕之后,方便不止一点点,展开机臂,开机,翻开遥控器天线,开遥控器电源(新的遥控器已经改为长按开/关机,比短按再长按又简洁了),拧上摇杠(基本上也懒得取下来了),就能飞了。一天的拍摄结束后,还可以将SD卡从机身取下插到遥控器上,直接在遥控器上选片/编辑。遥控器启动速度还比较ok,但扬声器效果就太一般了。

说完了优点也不是没有缺点,首先信号似乎不如以前,在建筑物较多地方,丢失信号几率挺高的。省掉了之前的LCD状态屏幕,看不到电机转速了。续航可以支持飞完4块电池,还是比较给力的。但是,遥控器充电需12V 2A充电器,目前看来只有QC 3.0的快充才完全符合,不然慢到基本冲不进。所以我的49元小米车充提前宣布淘汰,没有快充,充iPhone XS Max也是个麻烦。如果飞机和遥控器同时充电,车上还得要两个点烟器,恐怕大部分车都只有一个了吧。

新出的东西,配套还有待更新,比如220V充电器还是搭配microUSB的输出,USB-A端口不支持快充。内置Android 7的系统,虽然可以装App,也懒得折腾了。系统和遥控器飞控是互相独立的,也就是系统死机了,摇杠依然能控制飞机,虽然目前还没遇到过,相信和之前的设定应该是一样的。

Mavic 2 PRO with Smart Controller

官方资料:https://www.dji.com/cn/smart-controller