前言
这两天的工作又相对杂乱一些,处理一下A事情,又要搞一搞B事情,需要盯着C事情,还要尝试一下D事情,所以这篇总结没有什么主线,主要目的是记录一下最近解决的问题,先不展开讨论,当类似的问题积累一些再展开描述,我就先记录一下流水账了。
Redis问题
本来是一个常规清理数据,Redis回收内存碎片的操作,但是因redis-server版本问题被迫切换解决方案
启用碎片自动回收失败
1 | 127.0.0.1:6379> config set activedefrag yes |
开启主动碎片整理机制失败,提示错误大概意思是说,当前的 Redis 服务器未使用支持主动碎片整理(Active Defragmentation)的 Jemalloc 分配器,因为Redis 的主动碎片整理功能依赖于 Jemalloc 分配器,如果 Redis 是使用其他分配器(如 libc malloc)编译的,或者当前的 Jemalloc 缺乏必要的功能,就会导致该功能无法启用。
可以通过Redis命令 redis-cli INFO memory | grep allocator
来查询,如果显示 mem_allocator:jemalloc
,则表示启用了 Jemalloc。如果显示 mem_allocator:libc
,则说明当前未使用 Jemalloc,需要重新安装或编译 Redis。
我运行一看当然是 mem_allocator:libc
了,看来没办法进行碎片整理了,幸好是个slave节点,所以我干脆保存数据后重启一下吧。
启动Redis未脱离终端
首先关闭redis服务
1 | shutdown SAVE |
这个过程会进行同步存储,所以会给你一种卡死了的状态,因为我这里有90G数据,存储大概用了10多分钟,Redis服务成功关闭
然后按配置文件启动Redis
1 | /usr/local/bin/redis-server ./redis.conf |
启动完终端就停在这个了,难道我起了个前台程序?我记得之前都是这么启动的啊,总不能为了脱离终端我还要使用 nohup
和 &
来配合吧,有点low啊,主要是日志是不是就放到nohup.out文件里了,找找有没有配置吧,打开redis.conf文件发现daemonize no
,我的天啊,这个redis-server为什么这么与众不同,守护进程模式居然是关着的,虽然说把配置文件改成 daemonize yes
再启动就好了,但是我好奇的是之前是怎么启动的。
我决定不修改配置文件了,保持原来的样子,直接在启动时指定 --daemonize yes
好了,这样可以达到目的,在Linux系统中使用Redis时,命令行参数和配置文件参数(也称为Redis配置)具有不同的优先级。如果同一个配置选项在配置文件和命令行参数中被设置,那么命令行参数将覆盖配置文件中的设置。
Vercel问题
之前简单聊过将Nextjs框架编写的网站部署到Vecel有天然的适应性,因为Vercel背后的团队就是开发出Nextjs框架的那群人,部署到Vercel以后绑定一个域名就可以使用了,不需要自己安装运行环境,不需要配置DN缓存,不需要配置SSL证书,真是方便极了,但免费的账户是有资源限制的,特别是要团队开发的话需要购买付费版本。
未在Vecel团队的人提交无法触发自动部署
用过Vercel就会发现,当通过github导入项目之后,以后每次推送到github后都能自动触发Vercel的网站发布功能,但是未添加到Vercel团队的账号提交时无法触发自动部署,提示
Vercel - No GitHub account was found matching the commit author email address
一种办法就是把所有可能提交的账号都添加到Vercel团队,但是这是要花钱的,每添加一个成员每月$20,还有就是找已经在团队的成员再提交一次,触发自动部署就行了,这个成本也很低,也就是仓库记录不那么干净了而已
1 | git commit --allow-empty -m"trigger redeployment" |
更新package.json后部署Vercel时报错
因为我使用 npm install
初始化完项目后,启动时有两个警告
1 | Module not found: Can't resolve 'bufferutil' in 'E:\WorkSpace\nodeweb\qxweb\node_modules\ws\lib' |
所以我就手动安装了这两个库 npm isntall bufferutil utf-8-validate
,这就导致我的 package.json
文件更新内容里增加了这两个库的引用版本,提交代码部署Vercel时报错
1 | Running build in Washington, D.C., USA (East) – iad1 |
这个错误提示说明 Vercel 在部署过程中使用 pnpm
安装依赖时,遇到了 pnpm-lock.yaml
和 package.json
文件中的依赖不匹配问题。具体来说,pnpm-lock.yaml
文件中的依赖版本与 package.json
中声明的版本范围不一致,导致 pnpm
无法继续安装。
看到这时我才意识到我的项目里有个 pnpm-lock.yaml
,而我使用的 npm
安装,本地有个 package-lock.json
文件,这样一看是我工具用错了呀,我说这个项目怎么没提交 package-lock.json
文件呢
清理模块重新安装吧
1 | rm -rf .node_modules package-lock.json |
输出如下
1 | PS E:\WorkSpace\nodeweb\qxweb> pnpm install |
这次再启动项目 pnpm run dev
就不报缺失模块的警告了
简单说下 npm
和 pnpm
的关系,npm
是 Node.js 的默认包管理工具,用于安装、管理和共享 JavaScript 包。安装 Node.js 时会自带 npm,不需要额外安装。pnpm
是一种兼容 npm 和 yarn 的包管理工具。它与 npm 类似,也用来管理 JavaScript 包,但通过优化磁盘使用和依赖解析性能解决了 npm 的一些问题。
再简单说下 package.json
、package-lock.json
和 pnpm-lock.yaml
的关系
package.json
描述项目的基本信息(如项目名称、版本号),声明项目的依赖项、脚本命令和配置,是项目中最重要的依赖描述文件,是开发者直接编辑的文件,但不包含依赖的具体版本信息package-lock.json
是 npm 包管理工具生成的锁定文件,记录依赖的具体版本号和结构,确保不同环境中安装的依赖版本一致。不需要手动编辑,由 npm 自动生成和管理。pnpm-lock.yaml
是 pnpm 包管理工具生成的锁定文件,用于精确记录依赖的版本号和安装结构。确保团队或 CI/CD 环境在安装依赖时,所有人使用的依赖版本完全一致。不需要手动编辑,由 pnpm 自动生成和管理。
可以看到package-lock.json
和 pnpm-lock.yaml
是互斥的,平时 npm
和 pnpm
选择一个就行,别混着用。
Android问题
Apple推送选择APNs就好了,而Android推送一直就是老大难,因为FCM对谷歌框架的依赖,国内基本是不可用的,所以在之前的蛮荒年代,真是八仙过海各显其能,相互拉起、定时检测等等搞得手机卡顿的不行,后来各家厂商基本都实现了自己的通道,比如华为、小米、OPPO、Vivo等等,所以找个聚合的SDK也是能办到的,但是国外还是主要依赖FCM。
但随着Android版本的提升,推送的要求越来越严,一旦应用被强制杀死,FCM消息虽然能达到手机,但是不允许拉起应用,导致消息无法触及到用户,这也是目前国外环境要实现推送所面临的难点。
要想能收到FCM的推送消息,就得保证应用不被杀死,可能得方向有提高优先级、定时检测重启、杀死后立即重启、采用其他的推送通道等等,但是效果都不理想,之前好用的方式升级版本或者切换一个手机厂商就不太好使了,没有什么通用的解决办法。
后来发现,真对国内手机只要给应用开启自启动权限,那么即使被强杀后也能收到FCM通知,或许这是一个可以努力的方向,但这个权限不是通用的逻辑,每个手机厂商都有自己的白名单,大厂APP出生就在白名单里,而我们自己开发的APP需要经过复杂的引导操作才能加入其中,虽然很难,但终归是第一条可行的路。
主动请求通知权限
在 Android 13 (API 33) 及以上版本,应用需要 主动请求通知权限,以便能够发送通知。对于之前的版本,应用只需要在 AndroidManifest.xml
中声明 POST_NOTIFICATIONS
权限即可。然而,从 Android 13 开始,仅在声明权限的基础上,还需要在运行时申请此权限。
步骤1:修改 AndroidManifest.xml
首先,在 AndroidManifest.xml
文件中声明权限:1
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
步骤2:在应用中请求通知权限
从 Android 13 开始,你需要在运行时请求通知权限。你可以使用 NotificationManagerCompat
来检查和请求权限。
1 | import android.os.Build; |
步骤3:引导用户授权
如果用户没有授权通知权限,你通常需要提供一个方法引导他们到设置页面,在该页面中手动授权。因为从 Android 13 开始,权限只能通过系统设置进行手动授权,而不是通过应用内弹窗。
1 | private void goToSettings() { |
网络状态变化的监听不能使用静态注册
从 Android 7.0 (API 24) 开始,网络状态变化的监听不能使用静态注册 (<receiver>
标签在 AndroidManifest.xml
中注册),必须通过动态注册来实现。这是出于优化电池和性能的考虑,Android 不再允许应用通过静态注册的方式来监听系统广播(如网络变化、屏幕开关等),尤其是对敏感的系统事件。
静态注册 (AndroidManifest.xml)
静态注册会在 AndroidManifest.xml
中声明广播接收器,并且会在整个应用运行期间自动接收系统广播。对于网络状态变化(如 CONNECTIVITY_ACTION
)的静态注册从 Android 7.0 开始被限制。
AndroidManifest.xml 中的静态注册示例(在 Android 7.0 之前是有效的):
1
2
3
4
5<receiver android:name=".NetworkChangeReceiver">
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>
这种方式在 Android 7.0 及以后会被限制,无法接收到网络状态变化的广播。
动态注册 (代码中注册)
动态注册意味着在应用运行时,通过 Context.registerReceiver()
来注册接收器,这样可以选择性地在需要时注册,并在不需要时取消注册。这样做的好处是,可以更灵活地控制接收器的生命周期,避免不必要的电池消耗。
动态注册的示例:
1 | public class MainActivity extends AppCompatActivity { |
在这个示例中,使用 registerReceiver()
动态注册了一个监听网络状态变化的广播接收器 NetworkReceiver
,并在 onCreate()
中进行注册,在 onDestroy()
中进行注销,以避免内存泄漏。
各种Service介绍和对比
在 Android 中,Service
是一种后台组件,用于在应用中执行长时间运行的任务。根据服务的用途和生命周期,Android 提供了不同类型的服务。以下是 Android 中常见的 Service
类型和 BroadcastReceiver
的介绍及对比:
普通 Service (Normal Service) 没有前台 UI 组件。通常用于执行后台任务,不与用户交互,可以在应用的任何地方启动,并在后台运行,直到它完成工作或被显式停止,生命周期为 onCreate()
→ onStartCommand()
→ onDestroy()
,适用执行短时间的后台任务,如数据同步、文件下载等场景。
前台服务 (Foreground Service) 在运行时必须显示一个持续的通知(通知栏),因此它会在用户和系统资源管理中被认为是一个“重要”的服务,不容易被系统杀死。生命周期为 onCreate()
→ onStartCommand()
→ onDestroy()
(服务必须调用 startForeground()
来显示通知),适用执行长期任务,如播放音乐、导航、实时更新等场景。
1 | Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) |
JobService 是一种特殊类型的服务,它用于执行计划任务(即 JobScheduler
),在特定条件下(如网络连接、充电等)启动。JobService
适合用于执行延迟任务或条件任务,如周期性数据同步、定时上传等,与 JobScheduler
配合使用,可以在设备的空闲时间或在设备满足某些条件时执行任务。生命周期为 onCreate()
→ onStartJob()
→ onStopJob()
,适用执行定期任务,延迟任务,或者需要满足某些条件(如网络连接、充电等)的任务场景
1 | public class MyJobService extends JobService { |
BroadcastReceiver 是 Android 中用于接收广播的组件。它通过监听系统广播或自定义广播来响应事件(如网络状态变化、电池电量变化等)。BroadcastReceiver
不会启动一个独立的线程,它只会在接收到广播时执行相应的代码。onReceive()
方法在接收到广播时调用。适用监听和响应系统广播,如网络连接变化、设备开关机、短信接收、系统更新等场景
1 | public class NetworkChangeReceiver extends BroadcastReceiver { |
服务类型 | 生命周期 | 适用场景 | 优缺点 |
---|---|---|---|
普通 Service |
onCreate() → onStartCommand() → onDestroy() |
短时间的后台任务 | 简单易用,但容易被系统杀死,适用于短时间的任务 |
前台 ForegroundService |
onCreate() → onStartCommand() → onDestroy() |
长时间运行的任务,如播放音乐、导航 | 不容易被杀死,但需要显示通知,可能影响用户体验 |
JobService |
onCreate() → onStartJob() → onStopJob() |
执行定期任务、延迟任务,或者需要满足某些条件的任务 | 适用于需要在特定条件下执行的任务,延迟执行,但不能精确控制执行时机 |
BroadcastReceiver |
onReceive() |
响应广播事件,如网络状态变化、电池状态变化等 | 适合处理广播事件,不能执行长时间操作,执行时间受限制 |
总结
- Redis如果未使用Jemalloc无法开启主动的碎片回收,通过
redis-cli INFO memory | grep allocator
可查询内存分配器 - 启动Redis的方法
/usr/local/bin/redis-server ./redis.conf --daemonize yes
- 管理node模块时可以选择
nmp
或者pnmp
,不要混用,总的来说后者更优秀一点 - 管理Nextjs项目时最好吧
package.json
和package-lock.json
(pnpm-lock.yaml)都上传,便于安装出相同的运行环境 - 触发Vercel重新部署的命令
git commit --allow-empty -m"trigger redeployment"
- Android的服务有很多种,普通服务Service、前台服务ForegroundService,定时服务JobService等等
在自己的世界里独善其身,在别人的世界里顺其自然~
2024-11-29 15:50:46