Logo

site iconHolmesian

一些技术分享,Mac、树莓派等。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Holmesian RSS 预览

MacBook 休眠恢复不能上网问题

2022-02-10 13:49:24

最近发现在电池和交流电交叉使用期间,合上 MacBook 的盖子睡眠唤醒后,偶尔会出现 WiFi 能连上,并且能够获取到IP地址,但就是无法上网的情况。

没想到已经到 macOS Monterey 12.0.1 仍然还没有解决这个系统问题。

解决办法,手动重启airportd:

sudo killall airportd

macos.jpg

缓解 MacBook Pro 开网页的发热问题

2021-11-03 13:43:29

情况

我的 MBP(A1708)自从把 Mac 版 Onedriver 干掉换成自建的 Seafile ,再把 Mac 版 Docker 丢进虚拟机中的 Ubuntu 里之后,很少会出现风扇狂响的情况。

可是最近发现又会出现风扇狂转的情况,经过排查发现这回是 Chrome Version (95.0.4638.69) 导致,通过 Macs Fan Control 查看发现温度一直会飙升到 81°C 以上。 通过 Chrome 的 Task Manager(任务管理器) 查看发现 GPU Process (显卡管理进程) 长时间占用大量资源。

PS:Mac 版Chrome Task Manager 没有快捷键,只能通过 More Tools > Task Manager调出

原因分析

由于 Chrome 是资源老虎,资源占用高本不是什么稀罕事,但是无论什么程序,持续的高资源占用都是非常不正常的,正常情况下绝大多数程序都应该维持低资源占用。

chrome copy.png

打开的网页是充斥着特效比较多的页面,看来这次情况大概率是由于“GPU 硬件加速”功能起到反作用导致的。浏览器所提供的“GPU 硬件加速”功能最初的目的是通过使用 GPU 完成一些网页渲染工作来降低 CPU 负载的,然而对于 GPU 性能本来就捉襟见肘的设备,这个选项反而让实际情况南辕北辙了。

新版的 Chrome(包括Edge)都已经将在最近版本中将“GPU 硬件加速”预设为开启,所以需要手动关闭。

解决方法

在 Chrome 的网址栏列输入“chrome://flags/”,然后将“Accelerated 2D canvas”以及“GPU rasterization” 设置为 Disabled ,重启 Chrome 。

Screen Shot 2021-11-03 at 2.11.32 PM.png

风扇终于清净了……

再谈Go语言的交叉编译

2021-02-09 23:42:00

起因

之前有谈到过 Go 语言的交叉编译,虽然七七八八写了一堆,但是实际上的可操作性还是比较差的,当时使用go-ui-crossbuild项目也已经超过3年没有维护了。

golang-docker.jpg

最近从各个方面感受到了 docker 的方便,又恰好有个老项目需要跨平台编译,就顺手看了一下有没有类似 go-ui-crossbuild 的项目,结果才知道我真的是孤陋寡闻,因为不常用 Go,之前连大名鼎鼎的 xgo 项目都没有发现。

关于 xgo

简单地说 xgo 由两部分组成:一部分是一个(或者说一系列)已经预装好交叉编译环境的 docker 镜像;另一部分是 Go 语言编写的 xgo 可执行程序,xgo 通过调用准备好的 docker 镜像解决涉及到 CGO 的交叉编译问题,可达到一键编译多个发行版本或指定版本的效果。

这里先要大概要把我看到的 xgo 经历捋一捋:xgo项目最早是由 karalabe 大佬创建的,虽然几经发展,可是若干年前就没再更新,停滞的版本不支持Go mod,也存在一定的bug; techknowlogick 大佬 Fork 之后持续更新到支持Go mod,并且兼容到了 Go 1.13 ,但是之后也基本停滞了更新。接着 monkeywie 大佬修正了一些问题并且开始用 github actions 构建 docker 镜像。

PS:突然发现这几天 techknowlogick 大佬又开始活跃更新,做了不少修正,并且通过 github actions 实现了自动构建支持最新 golang 版本的 docker 镜像。

github actions 确实是个好东东。

毒打

作为一个小白,自然是要接受社会(网络)的毒打才能成长的。

我的初始需求是在 MacOS 平台上将一个涉及 CGO 的老项目 Go 源码编译出 win64 平台可以运行的可执行文件。

有了对 xgo 的了解自然就高高兴兴地用起来了。然而当我配置好 xgo 环境,在项目根目录满怀希望地运行

    xgo -v --targets=windows-6.1/amd64 . 

之后久久地沉默让我感觉自己遭受了莫名地毒打,还好当我看到形如


    " go: github.com/PuerkitoBio/[email protected]: Get https://proxy.golang.org/github.com/%21puerkito%21bio/goquery/@v/v1.6.1.mod: net/http: TLS handshake timeout  " 

字样的提示之后我就明白毒打我的是谁了。 (下载依赖包的时候遭受网络的毒打)

解决

如果是单纯要梯子的问题很容易解决,但是一方面我的现实情况是这个 docker 是跑在 Parallels Desktop 里面虚拟出来的 Ubuntu 上,这个环境有点套娃的意思,哪怕是从外到内的全局也有点烦,另一方面全局出去终究不是长久之计,于是我就想到了在 xgo 的镜像里添加 goproxy 配置来解决,同时也能够方便使用大陆网络的 TX 。

有遇到同样情况的TX可以直接用我修改的版本

    docker pull holmesian/xgo:latest
    
    go get github.com/holmesian/xgo

所有的用法和原版的xgo一样,只是在拉取依赖的时候使用了 goproxy 。

不放心的TX想自己修改也很简单:先 fork 大佬们的成果,再在 Base 镜像里的 Dockerfile 中加上

ENV GOPROXY=https://goproxy.cn ,direct 

大陆使用代理下载依赖,出错回退direct连接,接着GitHub Action 制作镜像推送到 docker hub,然后修改 xgo 里面的镜像地址编译出可执行文件就可以用啦。

后记

感谢 https://github.com/karalabe/xgohttps://github.com/techknowlogick/xgohttps://github.com/monkeyWie/xgo 等大佬们为 go 交叉编译做出的贡献。

开源的力量令人钦佩。

自行更换 MacBook Pro (A1708) 的电池

2020-11-04 00:42:00

老婆大人给我买的 MacBook Pro ,才200多循环电池最大余量就只剩下60%多了(说好能经受 1000 次折腾呢),查了一下相关资料感觉最近几年 MBP 的质量基本上都是在下滑,尤其是电池。

让人最不能忍的是无差别无规律的断电黑屏,重置 SMC(系统管理控制器), NVRAM 等等的方法用尽依然不能解决,今天官方授权点检测确认是电池的问题,鉴于过保售后更换要¥1773,于是自己动手淘了块原厂bq20z45 替换A1713电芯(bq20z451)。

我的这款机型在 iFixit 上的拆解评分为 1 果然不是盖的(表示非常难以拆解和维修),前后折腾了一个半小时,好在满血复活了,省下来的钱可以给S.H买AirPods了。

考虑到现在市面上 2017款 MacBook Pro的拆机教程都或多存在误导,这里就把相关的内容记录一下,希望能帮助到有需要的TX。

注:本文仅供参考,拆机有风险,并将彻底丧失原厂保修资格,动手前请三思。

写在前面

电池型号

2017款 MacBook Pro 有13寸、15寸,其中13寸分带TouchBar和不带TouchBar,不同的版本电池是不通用的,对应的关系如下:

新款无"Touch Bar" 13寸 A1708机型,电池型号:A1713
新款带"Touch Bar" 13寸 A1706机型,电池型号:A1819
新款带"Touch Bar" 15寸 A1707机型,电池型号:A1820

我的是 2017 13寸无 TouchBar 版(A1708),电池型号是 A1713 。因为根据网友的反馈,第三方的电池实在无法达到正常续航的预期,所以我选择了号称原厂的拆机电池(也是胆肥)。

原电池1.png

原电池2.png

更换前自带电池是用 TI 电源管理芯片 BQ20Z451 ,电池生产日期为2017年10月15日,电池固件版本901。

新电池1.png

新电池2.png

更换的电池是用 TI 电源管理芯片 是 BQ20Z45 ,电池生产日期为2016年10月9日,电池固件版本901。

更换的电池比原厂带的电池还大一岁,BQ20Z45 是通用物料,BQ20Z451是客户定制物料,固件一般会略有不同,目前充满电之后使用良好,暂时还没有遇到什么问题,等用一段时间之后再来反馈状况。

基本流程

网上找了一下 MacBook Pro (A1708) 换电池的视频,大多是引导把触摸板拆掉再铲电池,有的甚至是要把主板拆掉再铲电池,这类视频(地址1 地址2 )的步骤仅供参考,可能是考虑到电池底部的胶条实在太难铲除,担心下铲的时候把 触摸板FPC 或者其他元件碰坏所以才做进一步拆除,我是在基本没有拆除其他附件的情况情况下单换了电池。

大概流程是:拆开后盖->解下触摸板接口->拆除电池控制板->铲除电池底部胶条->拆除原电池->安放新电池->接上电池控制板->安装触摸板接口->安装后盖。

初次拆这款机器可能的难点应该在打开后盖、铲除电池底部胶条这两步,要特别小心的是拆除电池控制板的FPC,毕竟FPC很细接口卡扣又非常小,详细步骤和要点记录如下。

详细步骤

拆开后盖

初次拆这款机器可能第一个难关就是打开后盖,后盖有6颗螺丝,两两一组,用的是统一号梅花螺丝刀,对应的位置和螺丝的形状如下图。(为避免歧义,从背面看过去,以转轴在的长边(2颗螺丝边)为前方,4颗螺丝在的长边为后方)

背面.jpg

螺丝位置与形状.jpg

6颗螺丝取下来之后,用拨片从后方边撬开背面板,再以此将背板左右两侧的卡口打开,再稍用力向后抽出背板,如图所示。

拆开外壳.jpg

拆开外壳2.jpg

拆开外壳3.jpg

不出意外的话后盖就顺利打开了,接下来要取下触摸板接口上的排线,再来拆解电池控制板。

后盖打开照.jpg

解下触摸板接口

取下触摸板接口上的螺丝和压扣之后可以轻松取下排线。

拔出触摸板接口详细.jpg

拆除触摸板接口之后.JPG

揭开上图中电池电源板上条形胶纸,露出电池的控制板,建议从做往右揭开条形胶纸,右边不小心容易把排线拔下来。

拆除电池控制板连接

拆除胶条纸之后,看到的排线就是电池与主板的连接线了。排线左右两端均有小插头和卡扣,要把小卡扣掰成竖直状态再将排线从插头中拔出来,后续装排线的时候先插进去再小卡扣掰下去卡住。

拆除电源与主板之间的连接.JPG

取下主板与电池连接的排线之后,取下控制板上的3颗螺丝,中间那颗螺丝上有白色的纸片,应该是用来判断是否拆过电池的,无视或者取下就好。

搞定之后要开始重头戏,拆电芯了……

从左往右撕开胶条纸.JPG

铲除电池底部胶条

原厂电池三块电芯的底部是用胶条粘在机器上的,胶条的粘性非常的好,三块电芯建议先铲两侧的,熟练了之后再来铲中间的。

先放一个错误的铲法,千万不能这样直接撬,或者是一戳一戳的铲,伤机器、坏铲子。

错误的铲出法.jpg

正确的做法是先用铲子的一个胶切进去,根据胶条的位置顶进去,只要铲子顶进去开了小口,再不断扭动就很容易铲下来了。

正确的铲法.jpg

正确的铲法2.jpg

正确的铲法3.jpg

正确的铲法4.jpg

正确的铲法5.jpg

铲中间电芯的时候要特别注意,从左右两侧空余量比较多的一侧入手,结合胶条的位置参照两侧的铲法入手横着铲,千万要避开下方触摸板的排线,一个不小弄坏排线就亏了……

估计拆触摸板换电池的视频就是担心会破坏触摸板的排线。

把3颗电芯底部的胶条都铲开后很轻松的就可以取下旧电池。

终于铲下来了.jpg

安装新电池

电池铲下来之后强迫症可以清理胶条,我尝试着清理完左边电芯的胶条感觉很烦,反正后面换上去的电池也有胶条,就懒得清理了。

铲下来后清理胶条.jpg

新旧电池合影

新旧电池对比.jpg

换上去的电池比原电池年龄还大一岁,上机测试之后是0循环,希望后续别有什么毛病……

上新电池.jpg

电池很轻松地装上去了,装回电池与主板之间排线这里要特别提示一下

FPC右侧.jpg

FPC左侧.jpg

排线左右两侧的卡扣一定要按拆的顺序反向操作,排线插入插座必须足够长,否则后续回认不到电池。安装好电池后可以尝试开机测试,能找到电池没问题了再压实电池再往后装回后盖。

收尾

收尾部分没什么好说的,装回触摸板排线,反向操作安装回后盖。

测试开机.jpg

HAProxy 区分流量特征

2020-08-01 09:29:00

无意之中发现博客会间歇性无法访问,具体表现为部分请求长时间被 pengding ,一直处于 stalled 状态,复现条件不明确,时而出现时而正常。一开始还以为是这个评论中描述的因为OCSP 封装配置导致的问题,检查了一下配置和在服务器本地测试发现Nginx的配置并没有问题,经过日志分析将问题锁定到在 Nginx 前部的 Haproxy 上。

结构图片

本博按照之前443端口共用的方案一文中提到的方法已经跑了几年了,以往都没有出现这种间歇性无法访问的问题。仔细回想一下几个月之前貌似手贱升级过组件,是不是之前升级后的 Haproxy 版本功能差异导致了这个问题呢?

尝试原方案

带着这个疑问进一步地进行了测试,目前 HA-Proxy version 2.0.7 ,较 1.X 时代的版本确实有不小的变化,但是整体来说并没有什么过多的变化。随后发现,不仅 HTTPS 服务会出现间歇性失败的问题,复用的数据同样也会,于是我将焦点放到了Haproxy的流量区分规则上。

    listen https-in
        bind :443
        tcp-request inspect-delay 5s
        acl is_ssl req_ssl_ver 3:4
        tcp-request content accept if is_ssl
        server server-https :4431 check send-proxy
        use_backend ss-out if !is_ssl

acl is_ssl req_ssl_ver 3:4,之前是通过req_ssl_ver版本判断指令来确定流量是否属于 HTTPS 流量,然后再根据判断结果将非 HTTPS 流量转发到指定后端。

查阅资料发现 req_ssl_ver 已经废弃了,取而代之的是 req.ssl_ver 指令(注意 . 变成了 _)

    req.ssl_ver : integer
    req_ssl_ver : integer (deprecated)
    Returns an integer value containing the version of the SSL/TLS protocol of a
    stream present in the request buffer. Both SSLv2 hello messages and SSLv3
    messages are supported. TLSv1 is announced as SSL version 3.1. The value is
    composed of the major version multiplied by 65536, added to the minor
    version. Note that this only applies to raw contents found in the request
    buffer and not to contents deciphered via an SSL data layer, so this will not
    work with "bind" lines having the "ssl" option. The ACL version of the test
    matches against a decimal notation in the form MAJOR.MINOR (e.g. 3.1). This
    fetch is mostly used in ACL.

但是修改重试之后发现问题依旧…… 说明在这一版本里,光靠识别 SSL/TLS 协议的版本已经不能很好(或者说可靠)地区分流量特征了。

协议特征区分流量

有没有长久可靠一点的方法来区分流量呢,经过一番折腾找到了 req.payload 指令:

    req.payload(<offset>,<length>) : binary
    This extracts a binary block of <length> bytes and starting at byte <offset>
    in the request buffer. As a special case, if the <length> argument is zero,
    the the whole buffer from <offset> to the end is extracted. This can be used
    with ACLs in order to check for the presence of some content in a buffer at
    any location.
    
    ACL alternatives :
      payload(<offset>,<length>) : hex binary match

req.payload 是用来对数据流进行查找和标记的指令,和 req.ssl_ver 一样都是对 Haproxy 收到的原始数据流进行分析。

因为 HTTPS 协议的数据包的头部数据都是完全相同的(16 03 01),如果协议不发生大变化的话这个特征一直会稳定下去,所以优化后的完整配置如下:

listen https-in
    bind :443 tfo
    tcp-request inspect-delay 2s
    acl is_https req.payload(0,3) -m bin 160301
    tcp-request content accept if is_https
    use_backend ss-out if !is_https
    retry-on empty-response conn-failure
    server server-https :4431  send-proxy-v2 tfo

其中头尾的 tfo 和中间的 retry-on empty-response conn-failure 是为了开启 TCP Fast Open,非必须的。

再拓展一点

至此根据协议特征区分流量的目的已经达到,然而之前有一个网友留言问如何识别指定的特征,这其实就是自己提取和总结流量特征了。上面提到的协议特征是其他网友已经总结好了的,倘若你知道一个协议具有某些特征(例如 头部开头相同),或者是想故意制造某些特征(例如 某无特征流量 伪装成 HTTP/HTTPS 流量),可以按以下的方法自己尝试自己进行提取:

假设要提取特征的服务运行在1234端口上,在运行服务端执行:

nc -l -p 1234 | hexdump -C 

观察协议的特征。

然后在客户端尝试连接 1234 端口,观察服务端 nc 的输出。

以ssh服务为例,客户端使用 PuTTY 得到的输出

53 53 48 2d 32 2e 30 2d 50 75 54 54 59 5f 52 65 |SSH-2.0-PuTTY_Re|

头部的 53 53 48 便是特征值(根据偏移量的设置,可以挑取更长的,或者全局的)。

同理 HTTPS 服务的特征值是 16 03 01 。

HTTP 由于有 POST/GET、DEL、PUT 等不同的方法,头部的特征也不尽相同。

因此这三种协议的识别特征如下:

    #HTTPS
    acl is_https req.payload(0,3) -m bin 160301
    
    #GET POS(T) PUT DEL(ETE) OPT(IONS) HEA(D) CON(NECT) TRA(CE)
    acl is_http req.payload(0,3) -m bin 474554 504f53 505554 44454c 4f5054 484541 434f4e 545241
    
    #SSH
    acl is_ssh req.payload(0,3) -m bin 535348

好了,根据协议特征区分流量的就结束了,但是这些还不能解决网友在评论中提出的区分“tls1.2_ticket_auth”问题,这里推荐使用神器 haproxy 的内置针对原始流量中 SNI 信息进行筛选的 req.ssl_snile 指令,根据自定义的 SNI 区分流量。

    req.ssl_sni : string
    req_ssl_sni : string (deprecated)
    Returns a string containing the value of the Server Name TLS extension sent
    by a client in a TLS stream passing through the request buffer if the buffer
    contains data that parse as a complete SSL (v3 or superior) client hello
    message. Note that this only applies to raw contents found in the request
    buffer and not to contents deciphered via an SSL data layer, so this will not
    work with "bind" lines having the "ssl" option. SNI normally contains the
    name of the host the client tries to connect to (for recent browsers). SNI is
    useful for allowing or denying access to certain hosts when SSL/TLS is used
    by the client. This test was designed to be used with TCP request content
    inspection. If content switching is needed, it is recommended to first wait
    for a complete client hello (type 1), like in the example below. See also
    "ssl_fc_sni".
    
    ACL derivatives :
      req_ssl_sni : exact string match