ZhiWei Show

Just Show Me


  • Home

  • Tags

  • Archives

  • About

Docker-Swarm尝试引发的并发问题

Posted on 2020-05-18

背景问题

公司需要对用户的行为做分析
所以在服务端上通过 HTTP 的方式上报数据

想要将上报数据的网关容器化了
方面以后的维护和管理

因为节点少, 相对于 K8S 来说太重了.
所以准备尝试一下 Docker Swarm

没想到在测试环境没问题, 一上生产.
结果立马出现了一大堆上报失败的错误

吓的我立马回滚了操作, 再慢慢来排查.

Read more »

使用nginx代理跨机房的saltstack使用

Posted on 2020-05-10

问题背景

我们是游戏研发公司, 需要和不同的发行商合作运营
而且很多发行商要求使用自己的服务器(毕竟自己控制更可靠)
所以就出现了这么一种情况

业务不大, 但是不同云厂和区域的服务器不少.
并且我们使用 SaltStack 来管理主机的信息.
从主机的注册到作业的执行, 基本上是这样的

1
salt-master<----->nginx-stream<----->salt-minion

这里为啥使用 nginx-stream, 而不是 salt-syndic
主要是因为简单, 稳定, 而且 salt-syndic 无法针对单个 minion 下发 job

把 nginx 部署在docker swarm上
有一个区域就添加一个docker worker, 然后自动的部署上去了

大概的思路是这样的, 然而在部署好了后使用salt 'h72g9g19vr391.qcloud.hk' test.ping
有时正常, 有时却返回超时

Read more »

nginx的mirror模块请求返回400状态

Posted on 2020-05-10

问题背景

突然被抓来协助大数据的上报网关(OpenResty)

client -> OpenResty -> Kafka -> ClickHouse

这里的 OpenResty 负责数据的兼容, 过滤, 路由

因为需要经常的修改 OpenResty 的逻辑配合数据分析这边的需求
导致改动容易出现问题, 需要搞个测试环境.
通过测试环境的验证后, 在更新到正式环境

实施方案是使用 nginx 的 mirror 模块将请求镜像到测试站点.
同样的代码和配置, 通过环境变量来区分生产和测试环境

大概是这样的.

1
2
                                (不返回client)
client <-------------> nginx <-------------> mirror

那么问题来了
在实施中, 请求主站点时, 镜像站点也能收到请求
但一直显示status_code 400, 从表现上来看像是 POST 的数据丢失了.
在 nginx 的 log_format 把$request_body参数加上, 发现确实 POST 数据没有.

1
2
3
172.19.0.1 - [08/May/2020:06:35:09] "POST /user_login HTTP/1.0" 400 419 "-"
172.19.0.1 - [08/May/2020:07:01:09] "POST /user_login HTTP/1.0" 400 419 "-"
172.19.0.1 - [08/May/2020:07:02:43] "POST /user_login HTTP/1.0" 400 419 "-"
Read more »

客户端业务异常上报监控

Posted on 2020-04-03

背景

我们是一家 H5 的游戏公司
客户端以网页, 小程序, APP 的方式发布
对 CDN 的请求非常的大, 所以 CDN 请求的影响是非常大的.

前提

其实一开始是想放在 服务端业务异常日志监控 一起讲的
然而因为这里有一些情况不太一样, 细讲起来差异还挺大的.
所以这里就拆开单独写了(真的不是为了凑数!!!)

问题

  1. 客户端请求 CDN 卡顿
  2. 客户端请求服务端卡顿
  3. 客户端代码异常

方案

elasticsearch, 存储检索
logstash, 上报格式转换
kibana, 统计展示
openresty, 上报网关
(和”隔壁”的架构差不多, 只是用 openresty 替代了 filebeat)

看图…

Read more »

业务监控整理总结

Posted on 2020-03-19

监控范围

  1. 用户体验跟踪
  2. 应用质量跟踪
  3. 应用性能跟踪

跟踪相对指标, 不是绝对指标

监控分层

用户体验监控(前端)

因成本(研发成本)原因忽略用户手机的性能

只关注请求超时和请求错误的记录

因为成本(研发成本)原因无法获取所有的记录

所以我们使用总用户数作为比较的指标

Read more »

服务端业务异常日志监控

Posted on 2020-03-12

背景

我们是一家 H5 的游戏公司
多数项目的服务端使用 SKYNET+LUA 的形式开发
协议层走的的 websocket

问题

1
2
3
4
5
6
7
8
9
10
一般情况下我们都是走这样的流程

1. 业务更新版本
2. 等待用户反馈问题
3. 判断客户端还是服务端
4. 根据用户信息查看日志
5. 修复BUG更新版本

解决问题被动而且效率低下.
大量体验不好的用户直接流失.
Read more »

2019年的工作整理总结

Posted on 2020-02-07

2019 年终整理

运维平台

CMDB

我们目前已经接入了腾讯云的 dntg 项目

通过 canal+kafka 和额外的监控保证数据的及时性和准确性

在多项目和多云环境上还需要进行迭代完善

维护功能

正常的发展应该是 手工->脚本->平台->自动化 这个流程

然而我们直接抛开了平台这个阶段, 直接做了自动化.

导致我们后期的工作不好展开(主要是标准化的问题)

所以要将标准化的优先级提高

Read more »

阿里云ASK踩坑记录

Posted on 2020-01-12

背景

Ask 是 K8S 的一种特殊扩展, 它屏蔽了 node 层, 按 pod 收费.

这样可以实现低成本和弹性扩容, 在大规模的伸缩中极大的增加了效率和降低了成本

弹性扩展, 用多少算多少, 听起来确实很美好.

Ingress 本质上是一种抽象, 顾名思义, 就是所有请求的入口, 可以对请求进行控制和验证

常用的是 nginx 实现的 ingress, 通过规则映射, 将站点信息抽象各位一个个的匹配规则

接着, 我们有一个前端打点上报需求, 准备用阿里云的 ask 来做.

只是一个很简单的通过 http 请求将数据上报至阿里云 SLS 的服务.

没有状态, 存储之类的需求, 所以用 golang 写了一个简单的 api 来解决.

万事俱备, 等待数据, 然而研发同学说不太对劲.

Read more »

业务的自动化部署

Posted on 2019-12-20

背景

游戏行业的滚服需求, 服务之间相互独立
每天有大量的服务部署需求
并且在保证稳定的前提下尽可能的提高资源利用率

而且还有个问题, 我们没有购买主机的权限
需要先由我们下订单, 然后通过邮件通知合作方完成订单的支付
这就导致需要预留一定数量的主机, 避免支付响应不及时引发的运营事故

问题

我们这里解决问题的是如何实现自动部署
可能一些老司机说, 自动部署嘛, 这还不简单
写个页面, 然后调用 Ansible 一下子就完成了.

但是我们想要做的是完全自动化
整个流程无人介入(不出问题的情况下)

PS: 只是部署, 不负责发布上线

Read more »

Lua实现的hash合并功能

Posted on 2019-11-08

问题背景

业务需求要通过事件触发, 来实现两个 table 的数据合并.

理清了需求和思路并思考下扩展, 记录下来, 用于分享和沉淀.

因为用 lua 写的业务, 所以这里用 lua 实现的.

PS: 在 lua 里 table 和 hash 的结构类似

一层合并

数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
local a = {
["a"] = {
["aa"] = {
["aaa"] = "111"
},
["bb"] = {
["bbb"] = "222"
}
}
}

local b = {
["a"] = {
["aa"] = {
["aaa"] = "123123"
}
}
}

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function table.merge(dest, src, cover)
if type(dest) ~= "table" or type(src) ~= "table" then
return
end

-- don't use 'if not cover then'
-- because "(not nil or not false) == true"
-- T.T
if cover == nil then
cover = true
end

for k, v in pairs(src) do
if dest[k] then
if cover then
dest[k] = v
end
else
dest[k] = v
end
end
end

结果

1
2
3
4
5
6
7
{
["a"] = {
["aa"] = {
["aaa"] = "123123",
},
},
}

你看我想将 table B.a.aa.aaa 的值合并到 table A.a.aa.aaa

但是直接用 table B.a 将 table A.a 的值给覆盖了

所以这个函数只能用于一层 hash 函数的合并了

递归合并

数据

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
local a = {
["a"] = {
["aa"] = {
["aaa"] = "111"
}
},
["b"] = {
["bb"] = {
["bbb"] = "222"
}
},
["c"] = {
["cc"] = {
["ccc"] = "333"
}
}
}

local b = {
["a"] = {
["aa"] = {
["aaa"] = "111a"
["aaa1"] = "111a"
}
},
["b"] = {
["bb"] = 123123
},
["c"] = {
["cc"] = {
["ccc"] = {
["cccc"] = 3333,
}
}
}
}

函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function table.r_merge(dest, src, cover)
if type(dest) ~= "table" or type(src) ~= "table" then
return
end

if cover == nil then
cover = true
end

for k, v in pairs(src) do
if type(v) == "table" then
if dest[k] and type(dest[k]) == "table" then
table.r_merge(dest[k], v, cover)
elseif cover then
dest[k] = v
end
else
if cover then
dest[k] = v
end
end
end
end

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
["a"] = {
["aa"] = {
["aaa1"] = "111a",
["aaa"] = "111a",
},
},
["b"] = {
["bb"] = 123123,
},
["c"] = {
["cc"] = {
["ccc"] = {
["cccc"] = 3333,
},
},
},
}

table B.a.aa.aaa1 追加到了 table A.a.aa 里面

table B.a.aa.aaa 覆盖了 table A.a.aa.aaa

table B.b.bb 覆盖了 table A.b.aa

table B.c.cc.ccc 覆盖了 table A.c.cc.ccc

层级合并

扩展思考

一层合并和递归合并都不灵活

所以需要有一个提供给调用者想要合并层级的函数

数据

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
local A = {
["a"] = {
["aa"] = {
["xxx"] = "111"
},
["bb"] = {
["bbb"] = "222"
}
},
["b"] = {
["bb"] = {
["bbb"] = "222"
}
},
["c"] = {
["cc"] = {
["ccc"] = "333"
}
}
}

local B = {
["a"] = {
["aa"] = {
["aaa"] = "111",
["aaa1"] = "111a",
["aaaa"] = "1111",
["aaaaa"] = "11111",
},
["cc"] = "333",
},
["b"] = {
["bb"] = 123123
},
["c"] = {
["cc"] = {
["ccc"] = {
["cccc"] = 3333,
}
}
}
}

实现

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
function table.r_merge(dest, src, cover, depth)
if type(dest) ~= "table" or type(src) ~= "table" then
return
end

if type(depth) == "number" then
depth = depth - 1
elseif depth == nil then
-- 这里有点意思, 当depth == -1
-- 再次递归的时候永远都是负数.
-- 根据下面 depth == 0 的判断.
-- 就形成了无限递归的条件
depth = -1
else
return
end

if cover == nil then
cover = true
end

for k, v in pairs(src) do
if type(v) == "table" then
if cover and depth == 0 then
dest[k] = v
elseif dest[k] and type(dest[k]) == "table" then
table.r_merge(dest[k], v, cover, depth)
elseif dest[k] and cover then
dest[k] = v
end
else
if cover then
dest[k] = v
end
end
end
end

其实我不喜欢这样的函数.

因为在调用的时候就要明确的知道层级的范围是多少.

也就是说层级前和层级后的数据是不同的.

这种结构的不同是隐性存在的.

需要调用者自行把控.

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
["a"] = {
["bb"] = {
["bbb"] = "222",
},
["cc"] = "333",
["aa"] = {
["aaa1"] = "111a",
["aaaaa"] = "11111",
["aaa"] = "111",
["aaaa"] = "1111",
},
},
["b"] = {
["bb"] = 123123,
},
["c"] = {
["cc"] = {
["ccc"] = {
["cccc"] = 3333,
},
},
},
}

table A.a.bb 添加到了 table A.a

table B.aa 覆盖了 table A.a.aa, A.a.aa.xxx 没有了, 去哪了? ^.^

table A.a.bb 还在 A 里乖乖的呆着.

table B.b.bb 覆盖了 table A.b.bb

table B.c.cc 覆盖了 table A.c.cc

12

ZhiWei

20 posts
25 tags
© 2021 ZhiWei
Powered by Hexo
|
Theme — NexT.Pisces v5.1.4