V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  lesismal  ›  全部回复第 50 页 / 共 53 页
回复总数  1058
1 ... 42  43  44  45  46  47  48  49  50  51 ... 53  
2021-02-24 15:30:52 +08:00
回复了 howellz 创建的主题 Go 编程语言 golang 就没有提供一个可以被 cancel 的 read 接口?
试试我的异步网络库:

https://github.com/lesismal/nbio
2021-02-02 16:31:59 +08:00
回复了 yyyfor 创建的主题 程序员 关于系统瓶颈的面试问题
真热闹,我也补充一些吧

前面的各位都只是说到丢给 redis,但是单就 redis 也有击穿、穿透、雪崩各种说法,1s 几十万全丢给 redis,也只能说各位逮到个好玩意就往死里操,还是太暴力太浪费了

缓存和持久层其实都还是局部性原理的范畴,既然局部性原理,就再进一步,内存缓存,内存缓存这块跟 redis 放一块的话也有多种实现方式,比如:
1. 热点 key 的数据,定时或者发布订阅或者其他什么更新机制,服务节点 load 到自己的内存,请求来了直接返回自己内存缓存的
2. 同 key 访问加锁串行化,上一个请求回来后把结果带回来,其他等待锁的先检查是否有结果了,有了就直接拿结果、不落到 redis 了,相当于合并了到 redis 的请求。这个过程当然也可以结合或者改成内存缓存,比如内存的 1s 过期,内存没有、再 redis 、持久层之类的

高配点的机器,如果不是大 key 、value,几十万 qps 没啥压力,我自己的 i7 PC 测自己的 arpc 都能 40 多万 qps
@guonaihong 好,我 new 个 issue
@guonaihong 你试下我 12 楼和 16 楼的代码,两个 Post,我这里测,只打印了一组 begin/complete,不知道是不是我测试代码写错了,如果写错了楼主给指正下我再试试,如果没写错应该算是丢了个请求
上一楼打错字,"第二次发剩下的 1.5" 应该是 "第二次发剩下的 0.5"
@guonaihong 楼主先淡定点,不是开火的意思

我说没返回是指标准库返回了完整的 Request 结构体,Request 内已经把 URL/Header 各字段之类的解析好了,楼主的 httpparser 虽然 setting 里可以设置回调,但也是业务层自己需要二次加工,如果是对比性能,标准库相当于比你默认的 bench 代码多做了每个字段的解析,这样 bench 对比对标准库是不公平的

另外 1.5 个包的问题,比如我在 12 楼的测试代码,两个 http post 的数据,第一次发 1.5 个,第二次发剩下的 1.5,比如 setting 的回调这样:
var setting = httparser.Setting{
MessageBegin: func() {
fmt.Println("---- begin")
},
HeadersComplete: func() {
fmt.Println("---- complete")
},
}

只打印了一组
---- begin
---- complete

我没有去做更完整的测试和调试、不敢确定,提出来你看下算不算 bug,如果我看错了你解释就好了

技术交流,心态平和,需要豁达,不要火大 ^_^
只是解析出一个个包、不解析包内各段落具体字段相对简单,但是对实际工程帮助也不大,所以离工程使用还有很长距离
我尝试了上一楼的 1.5 个包,没法返回单个包给业务层。算是 bug
@guonaihong “设计的时候支持分段传入,内部是一个状态机。”—— 试一下一次读 1.5 个包的内容

var data = []byte(
"POST /joyent/http-parser HTTP/1.1\r\n" +
"Host: github.com\r\n" +
"DNT: 1\r\n" +
"Accept-Encoding: gzip, deflate, sdch\r\n" +
"Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n" +
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " +
"AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/39.0.2171.65 Safari/537.36\r\n" +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9," +
"image/webp,*/*;q=0.8\r\n" +
"Referer: https://github.com/joyent/http-parser\r\n" +
"Connection: keep-alive\r\n" +
"Transfer-Encoding: chunked\r\n" +
"Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n" +

"POST /joyent/http-parser HTTP/1.1\r\n" +
"Host: github.com\r\n" +
"DNT: 1\r\n" +
"Accept-Encoding: gzip, deflate, sdch\r\n" +
"Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4\r\n" +
"User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) " +
"AppleWebKit/537.36 (KHTML, like Gecko) " +
"Chrome/39.0.2171.65 Safari/537.36\r\n" +
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9," +
"image/webp,*/*;q=0.8\r\n" +
"Referer: https://github.com/joyent/http-parser\r\n" +
"Connection: keep-alive\r\n" +
"Transfer-Encoding: chunked\r\n" +
"Cache-Control: max-age=0\r\n\r\nb\r\nhello world\r\n0\r\n")

p := httparser.New( httparser.REQUEST)
fmt.Printf("req_len=%d\n", len(data)/2)
data1, data2 := data[:600], data[600:]
sucess, err := p.Execute(&setting, data1)
if err != nil {
panic(err.Error())
}
if sucess != len(data1) {
panic(fmt.Sprintf("sucess 111 length size:%d", sucess))
}

sucess, err = p.Execute(&setting, data2)
if err != nil {
panic(err.Error())
}
if sucess != len(data2) {
panic(fmt.Sprintf("sucess 222 length size:%d", sucess))
}

p.Reset()
@guonaihong "标准库的 http.ReadRequest,每秒只能处理 124MB 。相比之下 httparser 可以 300MB,性能还是可以的。" —— 这么说不太公平,标准库的是返回了 Request 、url header body 各段落字段都做了解析的
还想要 TLS 之类的支持,都搞细搞全了,也是个大工程。。。
我之前也想写一份 httpparser 来着,细想了下,没时间,放弃了。。。
大概看了下,不确定是否准确:
1. "粘包"可能有问题,不只是一个包可能拆成多段被应用层分多次读取到,也可能是多个包的数据放一块、被应用层从任意中间位置分多次读取到,比如 3 个包被两次读到、两次分别读到前 1.5 个和后 1.5 个包
2. 好像只是解析一个完整包的功能,并没有返回一个 Request/Response 类似的结构,所以 header 、body 之类的还是要业务层自己解析一道,这样的话业务层仍需要重复解析一次长度相关、比较浪费

建议也解析 header 、body 相关内容,一个完整包解析完之后返回一个 Request/Response 给业务层处理,在这基础之上 parser 内置 buf 的缓存,一个段落或者一个完整包后剩余的 half 部分由 parse 自己存上,有新数据来了加一块继续解析,这样业务层不必通过 success 再截断数据跟下次数据放一块,也免去重复解析 half 的浪费
2021-01-29 12:23:56 +08:00
回复了 fewuliu 创建的主题 Vue.js 尤雨溪承认 Vite 在之后会取代 Vue CLI,你开始学 Vite 了吗?
原生 js 足够用,只是以前的大多数前端工程师们缺乏工程性系统性思考。工程管理、性能优化,试试我这个:

https://www.v2ex.com/t/747412#reply8

https://www.v2ex.com/t/743590#reply20
端口数量通常不是问题,文件描述符上限设置个 10w 、100w,其他的几个内核参数设置合理就行,只要你硬件配置足够。不过还真有的站点设置的不合理,golang 中国报错文件打开数量过多我就遇到过好多次。socket 是 4 元组,单 IP 自己过来的最大端口 65535,不代表服务器对所有 IP 加起来只能 65535,而且单 IP 除非故意写 bug 或者攻击、否则也不至于有这么多,而且这些 CDN 、防火墙那里就能挡,还轮不到业务层来处理这个(并且业务曾代码也没有这个能力处理)

如果是觉得某个设备只要超过两三个连接就算过多,那就看我上一楼说过,限制机制自己订制下就好
另外,怕连接数过多的话,单节点配置好最大在线数,新连接进来的时候判断下、超过了就拒绝掉,这个可以在网关或者业务节点的 upgrader checkorigion 里做,更好点的方式是自己 wrap 下 net.Listener,serve(listener),Accept 的地方直接做
节点数不多、redis 的话,每个节点每秒 hset 、hmget 下就行了,没啥压力,而且实时性也足够
BTW,我这有个 ARPC 的 golang 框架提供了 websocket 聊天的简单示例

https://github.com/lesismal/arpc/tree/master/examples/webchat

另外 ARPC 支持发布订阅,如果想自己实现个管理服务器进行多个服务节点的在线数统计,管理服务器接收上报人数、然后把多节点的业务服在线总和发布就行了

想简单处理的话轮询写、读 redis 就好了
跨站点劫持楼主已经写了,check origin + 业务层认证

单设备连接数限制这个不太合理,通常应该按照身份限制比较好:既然有业务层认证,每个连接都有身份,如果不允许同一个身份多个连接、认证后就把之前的踢掉,如果允许,那就自己服务节点配置提高、节点数量增加之类的(如果怕统一身份的连接散到多个服务节点上,可以加个网关层,网关层按身份指定到实际的业务节点、由业务节点进行踢下线处理)。如果实在是想按照设备限制,那策略里使用身份的地方就改用 ip 或者你的算法能够生成的设备 id

统计人数通常不需要太精确,即使是多个服务节点,每个节点定时(比如 5s )更新自己节点在线数到 redis/sql 都可以、更新多节点在线数量总和就可以了,实时在线本来就是不停跳动的,精确的意义不大。如果实在要求精确,自己再写个服务进行统计、并且同步到所有节点,或者直接用 redis incr 之类的,每秒查询、更新,但是都没法保证百分百精确,实时的本来就是跳动的数据,即使是股票 K 线的蜡烛图也都是按时间段的起值、止值、最高值、最低值进行统计的
完整的例子可以看这里:
https://github.com/lesismal/pm/tree/master/examples/bind_src_dst

还有个项目是 fork 了个别人的 admin template,用 pm.js 改造了下,可以直接对比 fork 的原作,性能提升太明显了。。
https://github.com/3rdrepo/adminkit
@bmwh123 其实就是把老方案的内容摘出来放到单独的 html 子页面,index.html 里放上对应的 dom 元素,用 pm.bindPages 绑定这些 dom 元素的 id 和对应的子页面 url,可以手动 $pm.select 切换页面 也可以带上 src 配置项在对应的 dom onclick 时自动切换内容,或者配置的 dst 值就用 router,然后 $pm.listenRouter 用路由的方式自动切换,比如:

<div id="menu" class="side_bar">
<div style="height: 2px;">&nbsp;</div>
<button class="button">
<a href="#page_1" class="button">Page 1<br>Click Me</a>
</button>
<div style="height: 2px;">&nbsp;</div>
<button class="button">
<a href="#page_2">Page 2<br>Click Me</a>
</button>
</div>

dom 树上两个 <a> 分别路由到:
href="#page_1"
href="#page_2"

// 页面配置 && 绑定
let pages = [
{
dst: "page_1",
url: "page/page_1.html",
},
{
dst: "page_2",
url: "page/page_2.html",

// lazy 为 true 时则第一次显示时才进行加载,否则进入页面就开始加载
lazy: true,
},
];
$pm.bindPages(pages);

// 显示 page_1
$pm.select("page_1");

// 监听路由变化、自动切换页面,比如路由切换到 #page_2 时,相当于执行 $pm.select("page_2")
$pm.listenRouter();
1 ... 42  43  44  45  46  47  48  49  50  51 ... 53  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4840 人在线   最高记录 6547   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 37ms · UTC 10:07 · PVG 18:07 · LAX 03:07 · JFK 06:07
Developed with CodeLauncher
♥ Do have faith in what you're doing.