fanym's blog


  • 首页

  • 归档

  • 搜索

node在Typescrip中实现热更新的思路

发表于 2021-04-20

背景

其实 node 热更基本原理都是清除 require 的缓存后,在重新 require。之前也见过别人在 Typescrip 下的热更实现,但是有个缺点:重新 require 后缺失了引用对象的代码提示,造成了不好的开发体验。

实现记录

下面是我的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import * as fs from "fs";
import * as Card_role from "../data/Card_role.json";
import { LogUtils } from "./LogUtils";
import path = require("path");

const mLogUtils = new LogUtils(__filename);
/**
* 这里会将data目录下所有的文件绑定到HotReload上,同时会修改后热更新,但为了代码编写有提示所有还需要import文件,同时typeof到成员变量上
*/
export class HotReload {
// 为了开发效率这里就用一样的名字,还有这是为了代码中有提示才typeof
static Card_role: typeof Card_role;

public static init() {
HotReload.watchCleanCache("../data/", (fileName: string, moudle: any) => {
const name = fileName.split(".")[0];
HotReload[name] = moudle;
});
}

private static cleanCache(modulePath: string) {
delete require.cache[modulePath];
}

private static watchCleanCache(dir: string, cb: Function) {
const dirPath = path.join(__dirname, dir);
fs.readdir(dirPath, (err, files) => {
files.forEach((item) => {
const moudle = require(dirPath + item);
cb(item, moudle);
HotReload.watchFile(dirPath, item, cb);
});
});
}

private static watchFile(dirPath: string, fileName: string, cb: Function) {
let filePath = dirPath + fileName;
fs.watchFile(filePath, function () {
mLogUtils.info("HotReload :" + fileName);
HotReload.cleanCache(require.resolve(filePath));
const moudle = require(filePath);
cb(fileName, moudle);
});
}
}

这里顺便说下为什么用 watchFile 而不是 watch 方法,因为时间测试中修改文件一次出现 watch 方法回调了 3 次,虽然 3 次没什么影响,但是有节约强迫症改为了 watchFile,watchFile 有个小问题估计是轮询机制导致的就是有点延迟,但是可以接受。

为服务器添加定时日志清理

发表于 2021-03-29

背景

为了快速排查问题,我必加的日志就是接口的入参与返回。但是同事居然把框架自带的入参和结果代码注释了,理由是消耗服务器 io 与磁盘空间。为此我在论坛开贴讨论了,说 io 性能的由于日志很好分文件服务器写入且是顺序写入的,一般电脑顺序写入的速度可以超过 10MB/s,比起 io 性能更该担心的是服务器的磁盘空间,确实同事维护服务器经常要做清理日志的操作。为了降低这种清理工作量,有必要用下 Linux 的计划任务。

记录

打开 crontab

1
crontab -e

添加每天删除过期日志,减少磁盘空间占用

1
10 02 * * * find <查找路径> -type d -mtime +10|xargs rm -rf

拓展

其实还可以通过 log4j 来限制日志文件个数,这样就不用定时清理了。

关于severless的疑问和痛点

发表于 2020-09-05

1 、一个接口调用 severless 运行需要将所有函数接口代码加载到内存,运行玩之后就释放,还有对于数据库之类的连接就不是用连接池的方式要不断创建断开,这样运行效率不会比较低吗?

2 、使用了 severless 无法用之前开发的断点调试方式,也许应该是有办法的断点的,但是之前的开发者没有用断点,都是打日志的形式,这样调试起来效率就比较低了。

3 、接手的项目日志只有本地调试有打,部署后由于是 severless 无法存日志于本机,项目并没有做日志功能,如果是用户端反馈 bug,则排查起来比较困难。

4 、接手的项目并没有用主流的 web 框架来开发,而是自己对接口调用进来的像 url,get/post 进行 swich case 或 if 来处理的,这样无法与主流技术保持一致,我想对 koa 之类的框架应该有一些中间件实现对 severless 的支持吧,然后如果后面不想用来,可以比较方便地切换 severless 服务商或者运行在自己的服务器。

5 、部署不方便,要在 aws 的后台传 apigetway 文件,然后再传函数实现部分的代码,无法沿用我以前的 shell 脚本上传方式,加上 aws 网站太慢了,效率有点低,需要额外学习 aws 的开发文档然后写个 shell 脚本。

6 、项目中的 apigetway 文件是 swagger 的 yaml 文件,和接口实现部分是分开的,这样开发需要两个文件间跳转查看修改并保持一致,效率有点低,容易出错,我希望吧 swagger 的接口文档写在对应接口实现的注释中,然后转换成 aws 需要的 yaml 文件。

最后 severless 开发起来不够自由,无法内存长驻,无法使用自建服务器的技术,虽然降低了服务器开发维护的门槛,但是开发与云服务商强相关了,各家serveless的技术实现不统一,资料目前比较少。

利用废旧安卓手机做NAS的尝试

发表于 2020-07-31

背景

之前买了希捷的移动机械硬盘,但是接口接触不良,导致在高速读写时,硬盘损坏,而且正好有一台只是碎屏的小米 6x,于是就想将这个手机通过 otg 功能做为 NAS 用。这样好处就是:

1、手机占用空间小,耗电量低。
2、降低机械硬盘移动导致的意外损坏。
3、废物利用不用去购买一些额外的外设。

方案

之前用过 ftp 传输,但是 ftp 的缺点是无法在线浏览文件,使用起来不方便,最后选择了使用 samba 服务来实现,samba 服务支持 mac,windows,安卓,ios 使用,需要注意的是 windows 无法使用默认端口以外的端口,如果安卓需要用这个默认端口,就需要对安卓手机 root。

samba 服务有两种实现

一、个比较简单的方案是安装实现 samba 服务的 app,我找到的一个是叫 LAN drive 的 app,这个 app 比自己搭建 samba 服务简单多了,但是缺点是免费版速度被限制为 0.5MB/S,还有就是我未 root 时 mac 也出现连接失败的情况,root 后就可以了。

二、就是比较折腾的方案,在安卓上装上 linux 环境,然后在 linux 中运行 samba 服务实现,好处就是速度不受软件层面限制,而且自由度高也可以安装一些实现 NAS 功能的开源项目。缺点就是需要对 linux 命令等有一定了解,比较折腾。

综上,我选择了二方案,同时记录下一些遇到的问题。

实现

1、安装 linux 环境
先安装 Linux deploy 这个 app,然后先做如下配置,
然后点右上角菜单的安装,安装完成后,点配置,配置好就可以启动了,需要看下启动的日志中 ssh 服务是否启动成功,不成功的话一般是 linux 系统没装好,我试了几次翻墙重复安装才成功的。成功后可以 ssh 连接到手机 linux 安装 samba 服务。
需要注意下
1、镜像大小设为 5000MB。
2、启用 ssh。
3、将 otg 存储与手机内部存储挂载到 Linux。
4、最好在翻墙环境下载 linux 镜像。我出现了几次下载不成功的情况。
2、安装 samba 服务
安装不同 linux 发行版会有些区别,我用默认的 Debian,出现了 vim 方向键变字母的问题需要先升级下 vim。

然后通过ssh连接上后进行下面的操作:

1
sudo apt -y update

安装后出现方向键变字母的问题,需要升级下vim

1
sudo apt-get remove vim-common
1
sudo apt-get install vim

然后安装 samba

1
sudo apt -y install samba

添加 samba 用户,需要注意的是这里的用户名需要是 linux 系统的用户名,否则会失败

1
sudo smbpasswd -a root

配置/etc/samba/smb.conf,我的配置是在配置文件最后加上以下内容

1
2
3
4
5
6
[nas]
comment = share all
path = /mnt
browseable = yes
public = yes
writable = yes

最后启动 samba 服务生效

1
/etc/init.d/smbd restart

然后 mac 的话在 finder 用快捷键 comand+k 输入地址就可以就可以访问手机的存储了。

使用体验

由于我的路由器垃圾,只有 2-3MB/s的读写速度,换为手机热点就可以达到 16-18MB/s 的读写速度,都不是很快,但也够。最大的问题是手机存储不足,用otg挂机械硬盘好像供电不足,我买的带充电otg的线也只能挂U盘,很难成功带起机械硬盘,所以总体还是比较鸡肋的。感觉还是用开发板来做会更好。不过都装了Linux了,就可以把手机做服务器或者内网穿透工具了。

画洛伦兹曲线

发表于 2020-05-10

背景

最近 B 站的宣传片《后浪》又引起了大家对社会贫富差距加大对关注,所以想开发一个小程序来通过基尼系数来大致绘画出洛伦兹曲线来估计自己所在的财富位置。

算法

图片示例:
img

画洛伦兹曲线目前常被使用的方法主要有三种(引自百科):

几何计算法

即根据分组资料,按几何图形分块近似逼近计算的方法。

间接拟合法

即先拟合求出收入分配的概率密度函数,再根据概率密度函数导出洛伦兹曲线。

曲线拟合法

即选择适当的曲线直接拟合洛伦兹曲线,常用的曲线有二次曲线、指数曲线和幂函数曲线。

利用第一种方法不能得到洛伦兹曲线的表达式,只能用来计算基尼系数,但由于在计算分块面积时用直线近似地代替曲线,所估计的基尼系数要小于实际值,尤其在数据点较少时,误差较大。第二种方法由于计算收入分配的概率密度的复杂性,很难提出合适的概率函数。至于第三种方法,即直接用曲线方程去拟合洛伦兹曲线,应该不失为一种较好的方法,但目前主要的问题在于现有常用的曲线并不适用,曲线含义不明确,或拟合误差较大。

由于有效数据只有一个基尼系数,所以我选择曲线拟合法,曲线就用二次曲线,指数曲线,幂函数曲线都做一次吧。

考虑到函数曲线必经过(0,0)与(1,1)坐标点最终得到洛伦兹曲线公式为

二次曲线公式

1
y = a * Math.pow(x, 2) - (a - 1) * x;

指数曲线公式

1
y = (Math.pow(a, x) - 1) / a;

幂函数

1
y = Math.pow(x, a);

論文參考

在接近 0,1 时,感觉效果不理想。
想用简单采用直线与圆弧结合来画,但是這個算多段函數組合,不好計算,

最好參考了兩篇論文。

這篇是中科大的人 13 年寫的,他對我國基尼係數估計居然有 0.76 這麽高
Estimating Gini Coefficient Based on Hurun Report and
Poverty Line

對論文中的方程簡化后得

1
y = x - x * Math.pow(1 - x, a);

0-1 積分
其中 a>0 且 a<1

1
2
3
y = -(1 / 2 - 1 - 1 / (a + 2) + 1 / (a + 1));
轉化
a = (-3+Math.sqrt(9-4*(2- 1/(1/2 -y))))/2

導函數

1
y = 1 - Math.pow(1 - x, a) + (a * x) / Math.pow(1 - x, 1 - a);

簡化原因是論文的兩個參數都接近于 1,設為 1 對模型影響不大,且只有一個可變參數比較容易計算。

第二篇是一個國外的金融統計教授 12 年寫的

Modelling Lorenz curve

論文中的方程為

1
y = (Math.exp(a * x) - 1) / (Math.exp(a) - 1);

0-1 積分

1
y = (Math.exp(a) - 1) / (a * (Math.exp(a) - 1));

導函數

1
y = (Math.exp(a * x) - 1) / (Math.exp(a) - 1);

但这篇图像画出来后我觉得是不合理的。
最顶端的财富值与平均值的倍数应该远远大于平均值与最低端的倍数。因为无论多穷都需要有一个最低生存保障,而富人有多富一般人是想象不到的,同时
中国15年公布的5等分可支配收入也可以大致看出,高收入与中间收入比值是 2.30,中间收入与低收入比值的2.17,這兩個比值在比下為1.06。
中国19年公布的5等分可支配收入也可以大致看出,高收入与中间收入比值是 3.39,中间收入与低收入比值的3.05,這兩個比值在比下為1.11。
支持了财富值与平均值的倍数应该大于平均值与最低端的倍数,還可以判粗略斷出中國近幾年貧富分化還在加大。

所以为了更合理將这个的轉換后可得。

1
y = 1 - Math.log((1-x) * (Math.exp(a) - 1) + 1)/a;

暫時就用這兩個方程來模擬吧裏面的論文沒看太懂,還有就是很多論文要收費才可以看,或者看不懂。

开发

接下来就开始开发画二次曲线与幂函数曲线来拟合洛伦兹曲线的小程序。
开发框架选用 taro。
包含功能简介:

  1. 输入基尼系数画出洛伦兹曲线,
  2. 拖動坐標軸對應得到百分比的數值,和斜率。
  3. 對數據擬合得到模型,添加中國的五等分收入數據來擬合,可以推算收入水平在全國的百分比。
  4. 程序幫助説明。

img

mysql在加了索引两个字段在or的情况下全表扫描的问题处理

发表于 2020-04-26

背景

根据设备号imei和oaid来与渠道对接引流用户或激活,很容易想到imei与oaid加上索引来查找用户。然后很容易想到用这样的sql查询

1
SELECT count(1) FROM t_player_data WHERE  imei=<imei> OR oaid=<oaid>;

但是在阿里云的mysql后台中看到这个语句在一些情况下会是慢查询(最慢的在正式服上大约要12秒),需要查找下原因。

分析

表字结构(已过滤其他非关键字段),可以看到iemi,oaid都加上索引了,iemi是utf8mb4编码这个是历史遗留问题但和现在性能问题关系不大:

1
2
3
4
5
6
7
8
CREATE TABLE `t_player_data` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户真实id',
`imei` varchar(255) CHARACTER SET utf8mb4 DEFAULT '' COMMENT '设备码 --网站用',
`oaid` varchar(255) NOT NULL DEFAULT '' COMMENT 'android10无法获取imei用oaid代替',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_imei` (`imei`(191)) USING BTREE,
KEY `idx_oaid` (`oaid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1010638 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

需要分析sql性能一般是通过执行计划来看的,分析结果中比较关键的是type和row,这里对EXPLAIN的type做下说明:

链接类型 说明
system 表只有一行,MyISAM引擎。
const 常量连接,表最多只有一行匹配,通用用于主键或者唯一索引比较时
eq_ref 每次与之前的表合并行都只在该表读取一行,这是除了system,const之外最好的一种,特点是使用=,而且索引的所有部分都参与join且索引是主键或非空唯一键的索引
ref 如果每次只匹配少数行,那就是比较好的一种,使用=或<=>,可以是左覆盖索引或非主键或非唯一键
fulltext 全文搜索
ref_or_null 与ref类似,但包括NULL
index_merge 表示出现了索引合并优化(包括交集,并集以及交集之间的并集),但不包括跨表和全文索引。这个比较复杂,目前的理解是合并单表的范围索引扫描(如果成本估算比普通的range要更优的话)
unique_subquery 在in子查询中,就是value in (select…)把形如select unique_key_column的子查询替换。PS:所以不一定in子句中使用子查询就是低效的
index_subquery 同上,但把形如”select non_unique_key_column“的子查询替换
range 常数值的范围
index 索引树扫描。a.当查询是索引覆盖的,即所有数据均可从索引树获取的时候(Extra中有Using Index);b.以索引顺序从索引中查找数据行的全表扫描(无 Using Index);c.如果Extra中Using Index与Using Where同时出现的话,则是利用索引查找键值的意思;d.如单独出现,则是用读索引来代替读行,但不用于查找
all 全表扫描(full table scan).

上表至上而下效率越来越低。

在正式服测试:

1
2
3
4
EXPLAIN SELECT *shao FROM t_player_data WHERE imei='0' OR oaid='1'; --type ALL 
EXPLAIN SELECT * FROM t_player_data WHERE imei='1' OR oaid='0'; --type ALL
EXPLAIN SELECT * FROM t_player_data WHERE imei='0' OR oaid='1'; --type ALL
EXPLAIN SELECT * FROM t_player_data WHERE imei='1' OR oaid='1'; --type index_merge

可以看到执行计划在等于0的时候,就不走索引进行全表扫描了,同时row的条数也等于表条数了。

但在测试服测试中这4条语句的type就都是 index_merge,怀疑是imei和oaid为0的条数差异导致同样的语句链接类型的不同。
通过不断修改条数发现,row在1000条左右时链接类型会由index_merge 变为ALL 从而效率大幅降低,这个可能是mysql优化的规则具体原因没有找到。

解决

iemi与oaid在客户端获取不到的情况下,客户端都会给服务端传0,导致数据库中由大量这样的数据,然后在激活时又传0的设备码来查询就会出现全表扫描的慢查询。方案就是对传0的特殊处理,不再进入查询。

node对ProtoBuf解析进行日志输出的实现

发表于 2020-01-11

背景

项目开发的痛点之一没有输入输入日志,之前的框架设计没做好客户端输入输出的日志处理,导致服务端如果不断点,就无法知道客户端发了什么和收到什么。难以排查问题,降低了开发效率。现在需要在当前框架修改使其有客户端调用参数与回传的日志。

方案选择

为了解决这个问题考虑了4个方案来实现:

  1. 在框架调用的公共入口和出口进行数据打印。
    如果项目是json解析数据的是可以实现的,但当前项目采用了protobuff解析数据,在上层调用无法知道当前的解析对象,所以无法解析出数据。

  2. 在之前的接口实现功能的地方进行日志打印。
    实现接口功能时可以解析数据打印,但几百个接口工作量大而且代码复用性差,这是个可行但不好的方案。

  3. 修改proto解析对象的生成模板代码,对其加入日志输出。
    可以修改第三方代码来进行日志输出,但这种方案只能做为穷途末路的情况下的最后手段。首先修改第三方代码工作量大,容易出现问题,其次需要自己维护一个第三方的版本分支,以后无法升级第三方,最后增加后续维护接手的工作量。

  4. 通过高阶函数对proto 解析方法进行重新定义。
    这个方案修改了proto解析对象的内部方法,可以对所有proto解析进行进行日志输出,这个方案从代码复用性,工作量,可维护性,对第三方库的侵入性来综合考量是当前最好的解决方案。

综合上考虑,选择了第4个解决方案。

方案实现

具体实现如参考如下,代码量不多,只有些比较少用到的功能方法,代码为Typescript代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
import * as pro from '../../../../protoFiles/protoCompiled';
import { LogManager } from '../log/LogManager';

//需要忽略日志的proto
const ignoreProto = ['heartbeadResult'];
/**
* 对proto 的decode 重新定义输出日志
* @param fn 原函数
* @param cName proto name
*/
function funDecodeLog(fn: Function, cName: string): Function {
return function (...args: any[]) {
let res = fn.apply(this, args);
if (!cheakIsItemProto()) {
try {
LogManager.instance.oninfo(`${cName} decode proto message --->:${JSON.stringify(res)}`);
}
catch (e) {
LogManager.instance.ondebug(`${cName} decode proto Err :${e.message}`);
}
}
return res;
};
}

/**
* 对proto 的encode 重新定义输出日志
* @param fn 原函数
* @param cName proto name
*/
function funEncodeLog(fn: Function, cName: string): Function {
return function (...args: any[]) {
if (!cheakIsItemProto()) {
try {
LogManager.instance.oninfo(`${cName} encode proto message <---:${JSON.stringify(args[0])}`);
}
catch (e) {
LogManager.instance.onwarn(`${cName} encode proto Err :${e.message}`);
}
}
let res = fn.apply(this, args);
return res;
};
}

/**
* 检查是否是子proto
*/
function cheakIsItemProto() {
let myObj: any = {};
Error.captureStackTrace(myObj);
let str = String(myObj.stack);
let arr = str.split("\n");
return arr[3].indexOf('protoCompiled.js') > 0;//protoCompiled.js prot文件是生成的,根据对应的文件名设置
}

/**
* 重定义encode、decode 函数初始化入口
*/
export function initProtoFun() {
for (let cName in pro) {
if (ignoreProto.includes(cName)) {
continue;
}
if (typeof pro[cName]['decode'] === 'function') {
pro[cName]['decode'] = funDecodeLog(pro[cName]['decode'], cName);
}
if (typeof pro[cName]['encode'] === 'function') {
pro[cName]['encode'] = funEncodeLog(pro[cName]['encode'], cName);
}
}
}

实际上日志输出应该在框架层面封装好,但目前框架已经成形,这种实现从全局来看也并不完美。

进制在业务中的运用

发表于 2019-12-31

背景

之前策划是所有房间共存一个状态,但需求修改需要每个房间单独存一个状态,常规的解决方案是建表

  1. 建表加字段
    优点:代码结构清晰,业务需要更多状态存储也可以容易扩展
    缺点:需要建表,登陆需要多读一张表,多耗一些性能。业务扩展需要新建表字段。
  2. 通过进制在用一个数值存储多个房间状态
    优点:不需要改表结构,性能消耗和之前相当,扩展不需要新建字段
    缺点:需要对进制足够了解,且业务扩展受限,不能超过单个数的最大值。

具体用哪个方案需要根据业务场景,和扩展预期来选择,最后我选择了 2 方案通过 25 进制对六个房间数据存储,下面是对进制的公式做个记录

公式记录

初始化 n 进制的 ab 值为

1
a * Math.pow(n, 1) + b * Math.pow(n, 0);

将 n 进制的 p 的第 a 位数换为 c

1
2
3
Math.floor(p / Math.pow(25, a)) * Math.pow(25, a) +
(p % Math.pow(25, a - 1)) +
c * Math.pow(25, a - 1);

获取 n 进制 p 的第 a 位

1
Math.floor((p % Math.pow(n, a)) / Math.pow(25, a - 1));

总结

最后这个算法太骚了被禁止使用了。接手的人进制了解不够,代码就不好维护。

log4j在vscode中控制台无法输出日志的处理

发表于 2019-12-11

问题

项目中使用log4j来做日志输出管理,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
this.log4js = require('log4js');
this.log4js.configure({
appenders: {
out: { type: 'stdout' },//设置是否在控制台打印日志
ruleConsole: { type: 'console' },
ruleFile: {
type: 'dateFile',
filename: LogManager.instance.log_filename,
pattern: 'yyyy-MM-dd.log',
maxLogSize: 1024 * 1024 * 1024,
numBackups: 3,
alwaysIncludePattern: true
}
},
categories: {
default: {
appenders: ['out', 'ruleFile'],
level: "info"
}
}
});

然后出现了用webstrom可以正常输入日志,但vscode无法输出日志的情况。

解决方案

在.vscode/launch.json的启动配置加上 “outputCapture”: “std”, 就可以了。

1
2
3
4
5
6
7
8
9
10
11
{
"type": "node",
"request": "launch",
"name": "api_server",
"program": "${workspaceFolder}/bin/www",
"preLaunchTask": "tsc: build - tsconfig.json",
"outputCapture": "std",
"outFiles": [
"${workspaceFolder}/bin/**/*.js"
]
}

参考的issues

细数linux子系统WSL的坑

发表于 2019-12-11

背景

习惯用mac在类unix系统下开发,目前没配mac被迫用window下开发。用Windows开发效率太低了,无法用需要的命令和写shell脚本,还有一些与linux系统差异导致的坑。为了用上类unix之前尝试过:

  • 公司申请mac,好像没有回应了,不过配macmini也不够完美,习惯了mac触控板了。
  • 装黑苹果,没装成功,而且运维说有N卡问题会黑屏,放弃。
  • 装unbuntu虚拟机,太卡了。还有虚拟机无法启动的情况。
  • wsl目前最好的方案,估计是比较新的技术,有一些坑,而且坑的解决方案不好找。

具体的坑

  1. ssh和rsync的远程连接异常慢。
  2. 由于是远程连接开发,这种连接有时会出现无法连上的情况,需要重启vscode。
  3. linux子系统好像与window共享系统时间,linux下 date命令,ntp命令都无法修改系统时间,window修改系统时间linux系统也会变为对应时间。
  4. 毕竟是linux子系统的文件路径,在svn和日志中有一些文件路径会出现无法找到的情况,路径无法跳转,或svn日志无法查看,报错信息:
    1
    Not available from this working copy: file:///home/fanyingmao/svn/trunk/src/com/25qp/socket/handler/roomGuid/RoomGuideDealHandler.ts
  5. linux子系统中windows硬盘会被挂载在mnt目录下,反过来linux的文件会放在windows的 “ C:\Users<用户名>\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu18.04onWindows_79rhkp1fndgsc\LocalState\rootfs “ 路径中,但直接对路径中的文件修改linux不会立即同步修改,最后还是重启电脑。还有windows下创建的文件会有权限问题无法直接读写,需要改权限。
  6. 用autossh做内网穿透,有进程还在但穿透连接断开的情况,之前用虚拟机却没出现。
  7. 还发现一些命令工具无法使用如:docker,lsof,nmap。

总结

折腾得好累,人生苦短,给我配台 15寸的macbook pro 吧。

12…4下一页

fanyingmao

32 日志
43 标签
© 2021 fanyingmao
由 Hexo 强力驱动
|
主题 — NexT.Gemini v5.1.4
人 次