首页 文章 精选 留言 我的

精选列表

搜索[大型语言模型],共10000篇文章
优秀的个人博客,低调大师

大型分布式C++框架《二:大包处理过程》

本来这一篇是打算写包头在分布式平台中的具体变换过程的。其实文章已经写好了。但是想了这个应该是不能随便发表的。毕竟如果知道了一个包的具体每个字节的意义。能伪造包来攻击系统。其次来介绍一个包的具体变换过程意义不大。在每个分布式系统的里。包的扭转应该是个有不同。我们着重的应该是一种思想。一种共性。而不是个体的具体实现。 这里打算就介绍下大包的处理。其实这个更多的是介绍了下TCP切包。跟分布式没啥关系。。。。 不过这也算是系统的一部分 下面介绍下一个大包的具体处理过程 一、发送请求并分析 1)首先我们在客户端发送一个超过1M的包给客户端处理,结果是服务端只收了一次recv就拒绝了 2)为了更清晰 我们用tcpdump来抓包处理 注意由于是在本机上发送包接收包。所以走的是回路。即网卡lo 而不是eth0.抓包的时候 需要抓lo 否则看不到数据 1 [root@localhost git] # tcpdump -i lo port 53101 -w ./target.cap 拿到数据以后放到wireshark里分析 a)为什么最大协商是16396 而实际收了16384个字节 可以看到协商的mss最大传输单元为16396 但是显示netio revc只收到了16384个字节。 因为TCP包头中会带有12字节的选项----时间戳 所以实际是16396-12 =16384 所以neito一次收到的是16384个字节。 注意这里的测试环境是本机回路。 如果是在公网上。 一次TCP能承载的最大数据包 应该是 1448(1500MTU-20 IP包头 -20TCP包头 -12时间戳) (具体且包的原理请看下面) 能revc的数据跟TCP的接收缓冲队列有关。 b)为什么本机就只收了一次 mss ? sysctl -a | grep net.ipv4.tcp_wmem net.ipv4.tcp_wmem = 4096 16384 81920 第二个值是send默认发送缓冲区字节的个数 所以send一次16384个字节然后mss的大小刚好协商的是16396。 所以就被recv一次收到了。 正常在公网下。send 的16384个字节会被TCP切成11个包发到netio。 TCP会在接受端组包。但是不一定会一下都收到这11个包。可能就收到5个 然后组包给recv来处理。然后继续循环recv。还收下面的6个包.组包给recv 所以netio会根据缓冲队列已经网路情况 recv到部分字节或者全部的16384个字节。 这里如果send的数据大于16384 个字节。那么就是循环上面的步骤. c)为什么服务端拿到一次recv就之间关闭请求了。 因为我们服务端允许客户端传来的请求必须小于1M.所以拿到一次recv以后。就可以解析包头。发现客户端到底需要发送多少个字节。 超过1M 我们认为就是非法包。直接拒绝并关闭客户端连接。 二、接下来我们来分析一个服务端能处理的大包 1、我们发送一个262360 个字节包给服务端 2、这里注意下epoll收包的写法 当recv完一个包的时候。如果正确。就会回到最开始的while循环然后继续监听 epoll。然后会触发E_TCP_CONN事件。 看如下图。收完一个recv就会跳到while。继续epoll wait 来收下一包。反复如此直到收完一个完整的包 注意早期的写法是这样的。直接在while循环里收recv。 这是在阻塞机制下的写法。 我们用到epoll. 可以不用阻塞在recv 可以等待事件触发读取。 可以自己比对下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 while (1) { cnt = ( int )recv(m_socket, pBuf,RECVSIZE, 0); if ( cnt >0 ) { //正常处理数据 } else { if ((cnt<0) &&( errno == EAGAIN|| errno == EWOULDBLOCK|| errno == EINTR)) { continue ; //继续接收数据 } break ; //跳出接收循环 } } 3、结果以及分析 1 2 3 4 5 6 7 8 9 10 11 12 最后收到的结果如下 收了11次。总共262360个字节 m_iRecvBufLen:16384 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:16384 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:32768 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:16384 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:16384 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:65536 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:32768 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:16384 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:16384 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:16384 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 m_iRecvBufLen:16602 sizeof (m_achRecvBuf):131072 TPT_RECV_BUF_LEN:131072 我们看到每次发送的length都是16384 但是由于滑动窗口win。时大时小。发送的速度。不一样。导致recv一次能收到的数据也是不一样的 收到这些包。把他们组成一个完整的包。即发送端发过来的262360的包 开始跳到intCNetioApp::OnRecv(intiTcpHandle,char*pBuffer,uint32_tnBufLen)处理 后面就是常规逻辑了。丢到container的请求消息队列。 然后container处理完丢到netio的回包队列。 最后netio拿到包返回给前端 这里最后总结: 1、这里对分布式平台来说。会有一个专门收包的进程。只负责收包和转发。本身没有业务处理。它收到包以后会打上时间戳或者其他一些基本信息。然后把包丢给其他业务进程处理。然后等包回来了在把处理完后的包返回给前端。如果需要请求的服务不再本机。会有另一个转发器。把请求包发送大分布式中的其他服务处理。这里先说下大概的功能。后面会单独开篇介绍我们的netio。 2、收包并不是一次全收完才可以处理。一般一次recv以后。拿到固定包头信息就可以来判断包的一些基本状态。可以分析是不是系统想要的包、允许的包 3、理解下TCP的一些基本原理。 a)假如我们在TCP层用更大的数据量来打包会有什么结果呢? 答案是降低了传输效率。 分片最大的坏处就是降低了传输性能,本来一次可以搞定的事情,分成多次搞定,所以在网络层更高一层(就是传输层)的实现中往往会对此加以注意! 这个就是在以太网上,TCP不发大包,反而发送1448小包的原因。只要这个值TCP才能对链路进行效能最高的利用。 所以TCP在发包的时候已经切好大小刚好的包。不需要IP层再去切包 b)简单来说 滑动窗口是用来控制发送速度的

优秀的个人博客,低调大师

go语言

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 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 packagecontrollers import( "github.com/revel/revel" "fmt" "time" "strconv" "strings" "database/sql" "text/template" _ "github.com/go-sql-driver/mysql" ) typeSparkstruct{ *revel.Controller } var ( starlist=map[string]string{ "xuezhiqian" : "薛之谦" , "wanghan" : "汪涵" , "yangdi" : "杨迪" , "guoxuefu" : "郭雪芙" , "zhangyu" : "张宇" , "liuwei" : "刘维" , "tianyuan" : "田源" , "qianfeng" : "钱枫" , "shenmengchen" : "沈梦辰" , "zhaosilu" : "赵思璐" , } db*sql.DB ) typedbObjstruct{ db*sql.DB } func(cSpark)getcase()*dbObj{ if db!=nil{ return &dbObj{db:db} } else { var confmap[string]string conf=c.getconf() fmt.Println(conf) server:=fmt.Sprintf( "%s:%s@tcp(%s:%s)/%s?charset=%s" ,conf[ "user" ],conf[ "pwd" ],conf[ "host" ],conf[ "port" ],conf[ "db" ],conf[ "charset" ]) db,_=sql.Open( "mysql" ,server) //第一个参数为驱动名 db.SetMaxOpenConns(100) db.SetMaxIdleConns(10) return &dbObj{db} } } func(cSpark)getdb(){ if db==nil{ var confmap[string]string conf=c.getconf() fmt.Println(conf) server:=fmt.Sprintf( "%s:%s@tcp(%s:%s)/%s?charset=%s" ,conf[ "user" ],conf[ "pwd" ],conf[ "host" ],conf[ "port" ],conf[ "db" ],conf[ "charset" ]) db,_=sql.Open( "mysql" ,server) //第一个参数为驱动名 db.SetMaxOpenConns(100) db.SetMaxIdleConns(10) } } func(cSpark)getconf()map[string]string{ var confmap[string]string conf=make(map[string]string,10) conf[ "host" ],_=revel.Config.String( "host" ) conf[ "db" ],_=revel.Config.String( "db" ) conf[ "pwd" ],_=revel.Config.String( "pwd" ) conf[ "user" ],_=revel.Config.String( "user" ) conf[ "port" ],_=revel.Config.String( "port" ) conf[ "charset" ],_=revel.Config.String( "charset" ) return conf } func(cSpark)Add(starnamestring,urlstring,usernamestring,cbstring)revel.Result{ starname=template.HTMLEscapeString(strings.TrimSpace(starname)) username=template.HTMLEscapeString(strings.TrimSpace(username)) url=template.HTMLEscapeString(strings.TrimSpace(url)) if starname== "" { return c.display(1, "请填写好你支持的明星,再提交!" , "" ,cb) } if url== "" { return c.display(2, "请填写好你剪辑好的视频,再提交!" , "" ,cb) } newlist:=make(map[string]string,len(starlist)) for key,name:=rangestarlist{ newlist[name]=key } _,ok:=newlist[starname] if (!ok){ return c.display(3, "填写的明星不存在" , "" ,cb) } t:=time.Now().Unix() tt:=strconv.FormatInt(t,10) //c.getdb() c.getcase() //stmt,err:=db.Prepare("insertintostar(starname,username,videourl,addtime,status)values(?,?,?,?,?)") //checkErr(err) //res,err:=stmt.Exec(starname,username,url,tt,0) //checkErr(err) //id,err:=res.LastInsertId() //fmt.Println(id) fmt.Println(db) _,err:=db. Exec ( "insertintostar(starname,username,videourl,addtime,status)values(?,?,?,?,?)" ,starname,username,url,tt,0) _,err=db. Exec ( "updateinfosetnum=num+1wherestarname=?" ,starname) //checkErr(err) //id,err=res.LastInsertId() if err==nil{ return c.display(0, "ok" , "提交成功,请耐心等待工作人员审核" ,cb) } else { return c.display(4, "插入数据库失败" , "" ,cb) } } func(cSpark)Getstarlist(cbstring)revel.Result{ c.getcase() rows,err:=db.Query( "selectid,starname,num,imgurl,videourl,headimgfrominfoorderbynumdesc" ) checkErr(err) deferrows.Close() list:=make([]map[string] interface {},0,1) for rows.Next(){ row:=make(map[string] interface {}) var idint var starnamestring var numint var imgurlstring var videourlstring var headimgstring err=rows.Scan(&id,&starname,&num,&imgurl,&videourl,&headimg) row[ "id" ]=id row[ "starname" ]=starname row[ "num" ]=num row[ "imgurl" ]=imgurl row[ "videourl" ]=videourl row[ "headimg" ]=headimg checkErr(err) list=append(list,row) } return c.display(0, "ok" ,list,cb) } func(cSpark)Getone(namestring,cbstring)revel.Result{ //c.getdb() c.getcase() stmt,err:=db.Prepare( "selectid,starname,num,imgurl,videourl,headimgfrominfowherestarname=?" ) checkErr(err) var idint var starnamestring var numint var imgurlstring var videourlstring var headimgstring error:=stmt.QueryRow(name).Scan(&id,&starname,&num,&imgurl,&videourl,&headimg) checkErr(error) row:=make(map[string] interface {}) row[ "id" ]=id row[ "starname" ]=starname row[ "num" ]=num row[ "imgurl" ]=imgurl row[ "videourl" ]=videourl row[ "headimg" ]=headimg return c.display(0, "ok" ,row,cb) } funccheckErr(errerror){ if err!=nil{ panic(err) } } func(cSpark)display(errnoint,msgstring,data interface {},cbstring)revel.Result{ var result=make(map[string] interface {},2) result[ "errno" ]=errno result[ "msg" ]=msg result[ "data" ]=data if cb!= "" { return c.RenderJSONP(cb,result) } else { return c.RenderJSON(result) } } 本文转自 陈小龙哈 51CTO博客,原文链接:http://blog.51cto.com/chenxiaolong/1944665

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册