111-2024-07-07周总结

  1. konga平台跨域问题
  2. mongo拉数据问题

1. konga平台跨域问题

之前一直用腾讯云的Api网关用来做业务分发,最近上去看到Api网关要废弃了,推荐用云原生Api网关替换,在上面部署了个新业务,从实用性上来说没多大差别,唯一的问题就是一个service和一个后端绑定了,虽然可以配置多个路由,但最终都会指向到一个后端。这就导致一个后端需要建一个service,而我的业务中,基本上都是一个路由对应一个后端,对我来说就比较繁琐。之前Api网关是在配置路由的时候可以选择后端通道的,这样就能随意配置。

另外还有个问题就是云原生Api网关配置跨域比较麻烦,在腾讯云后台还不能直接配置,需要去他提供的konga后台去配置cors插件,而且配置也比较繁琐,也不支持导入导出插件功能,需要一个个手动填,比较麻烦。另外在支持跨域的时候需要在路由那边支持OPTIONS请求,因为我的请求是非简单。具体可以参考这篇文章

2. mongo拉数据问题

新项目上线之后,策划有需求要拉下线上数据。因为用的mongo数据,不太熟悉,而且拉数据方式跟sql不太一样,所以拉起数据来比麻烦,需要写JavaScript脚本,比如拉取次留的写法:

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// 定义起始日期 (UTC+8 时间)
var startDate = new Date(2024, 5, 29); // 请根据实际情况调整年份和月份(月份是从0开始的)
var endDate = new Date();

// 转换为时间戳
var startTimestamp = startDate.getTime() / 1000;
var endTimestamp = Math.floor(endDate.getTime() / 1000);
print(startTimestamp);
print(endTimestamp);

// 初始化结果数组
var retentionRates = [];

// 循环每一天
var currentTimestamp = startTimestamp;
while (currentTimestamp < endTimestamp) {
// 查询当天注册的用户
var registrations = db.LogRegister.find({
Day: currentTimestamp
}).map(function(r) { return r.PlayerId; });

var registeredUsers = new Set(registrations);
var registeredUsersCount = registeredUsers.size;

// 查询第二天登录的用户
var logins = db.LogLogin.find({
Day: currentTimestamp + 86400 * 1
}).map(function(l) { return l.PlayerId; });

var loggedInUsers2 = new Set(logins);

// 查询第三天登录的用户
logins = db.LogLogin.find({
Day: currentTimestamp + 86400 * 2
}).map(function(l) { return l.PlayerId; });

var loggedInUsers3 = new Set(logins);

// 查询第四天登录的用户
logins = db.LogLogin.find({
Day: currentTimestamp + 86400 * 3
}).map(function(l) { return l.PlayerId; });

var loggedInUsers4 = new Set(logins);

// 查询第五天登录的用户
logins = db.LogLogin.find({
Day: currentTimestamp + 86400 * 4
}).map(function(l) { return l.PlayerId; });

var loggedInUsers5 = new Set(logins);

// 查询第六天登录的用户
logins = db.LogLogin.find({
Day: currentTimestamp + 86400 * 5
}).map(function(l) { return l.PlayerId; });

var loggedInUsers6 = new Set(logins);

// 查询第七天登录的用户
logins = db.LogLogin.find({
Day: currentTimestamp + 86400 * 6
}).map(function(l) { return l.PlayerId; });

var loggedInUsers7 = new Set(logins);

// 计算留存用户
var retainedUsersCount2 = Array.from(registeredUsers).filter(function(id) {
return loggedInUsers2.has(id);
}).length;

var retainedUsersCount3 = Array.from(registeredUsers).filter(function(id) {
return loggedInUsers3.has(id);
}).length;

var retainedUsersCount4 = Array.from(registeredUsers).filter(function(id) {
return loggedInUsers4.has(id);
}).length;

var retainedUsersCount5 = Array.from(registeredUsers).filter(function(id) {
return loggedInUsers5.has(id);
}).length;

var retainedUsersCount6 = Array.from(registeredUsers).filter(function(id) {
return loggedInUsers6.has(id);
}).length;

var retainedUsersCount7 = Array.from(registeredUsers).filter(function(id) {
return loggedInUsers7.has(id);
}).length;

// 计算留存率
var retentionRate = registeredUsersCount > 0 ? (retainedUsersCount2 / registeredUsersCount) : 0;

// 打印结果
var d = new Date(currentTimestamp * 1000);
print((d.getMonth() + 1).toString() + "-" + d.getDate() + ", 新增人数: " + registeredUsersCount +
", 次留: " + ((retainedUsersCount2 / registeredUsersCount) * 100).toFixed(2) + "%" +
", 3留: " + ((retainedUsersCount3 / registeredUsersCount) * 100).toFixed(2) + "%" +
", 4留: " + ((retainedUsersCount4 / registeredUsersCount) * 100).toFixed(2) + "%" +
", 5留: " + ((retainedUsersCount5 / registeredUsersCount) * 100).toFixed(2) + "%" +
", 6留: " + ((retainedUsersCount6 / registeredUsersCount) * 100).toFixed(2) + "%" +
", 7留: " + ((retainedUsersCount7 / registeredUsersCount) * 100).toFixed(2) + "%");

// 移动到下一天
currentTimestamp += 86400;
}

这个可以做优化,可以先把每天注册的用户和登录的用户一次性都拉到,循环里面只做计算即可。

这个看着还好,但是我要拉取某些用户数据的时候就很麻烦,而且不容易看懂。比如我要拉取所有玩家的等级数据:

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
db.Player.aggregate([
{
$addFields: {
CreateDate: {
$dateToString: { format: "%Y-%m-%d", date: { $toDate: { $multiply: ["$CreateTime", 1000] } } }
},
LoginDate: {
$dateToString: { format: "%Y-%m-%d", date: { $toDate: { $multiply: ["$LoginTime", 1000] } } }
}
}
},
{
$match: {
$expr: { $gt: [ "$LoginDate", "$CreateDate" ] }
}
},
{
$addFields: {
Modules: { $arrayElemAt: [ { $arrayElemAt: [ { $arrayElemAt: ["$Player", 0] }, 3 ] }, 1 ] }
}
},

// 等级
{
$addFields: {
BasicModule: { $arrayElemAt: [ { $arrayElemAt: [ { $arrayElemAt: [ "$Modules", 1 ] }, 1 ] }, 0] }
}
},
{
$addFields: {
Level: { $arrayElemAt: [ "$BasicModule", 0 ] }
}
},
{
$project: {
_id: 0,
Gbid: 1,
CreateDate: 1,
LoginDate: 1,
Level: 1
}
}
]);

里面都是arrayElemAt,不太直观,很容易搞不清楚当前到哪一层了。这里框架这边也有些设计问题在,所有的Dictionary和Object都会转成array来处理,主要原因是因为Mongo不支持int作为key来存数据,然后为了避免单个文档不超过16mb限制(mongo有这个限制),所以每个字段是不存key的,通过代码里面的属性标签来确定到底是array的哪个字段。这样导致的问题就是要找Player某个字段会很麻烦且不直观。这一块后续再想想怎么去优化下框架设计。