游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

使用Nginx构建前端日志统计服务(打点采集)服务

  工作中经常会遇到需要数据支撑决策的时候,那么可曾想过这些数据从何而来呢?如果业务涉及Web服务,那么这些数据的来源之一便是服务器上各种服务器的请求数据,如果我们将专门用于统计的数据进行服务器区分,有一些服务器专注于接收统计类型的请求,那么产生的这些日志便是打点日志。
  本文将介绍如何在容器中使用Nginx简单搭建一个支持前端使用的统计(打点采集)服务,避免引入过多的技术栈,徒增维护成本。写在前面
  不知你是否想过一个问题,当一个页面中的打点事件比较多的时候,页面打开的瞬间将同时发起无数请求,此刻非宽带环境下用户体验将不复存在,打点服务器也将面临来自友军的业务DDoS行为。
  所以这几年中,不断有公司将数据统计方案由GET切换为POST方案,结合自研定制的SDK,对客户端的数据统计进行进行打包合并,并进行有一定频率的增量日志上报,极大的解决了前端性能问题、以及降低了服务器的压力。
  五年前,我曾分享过如何构建易于扩展的前端统计脚本,感兴趣可以进行关联阅读。POST请求在Nginx环境下的问题
  看到这个小节的标题,你或许会感到迷惑,日常对Nginx进行POST交互司空见惯,会有什么问题呢?
  我们不妨做一个小实验,使用容器启动一个Nginx服务:dockerrunrmitp3000:80nginx:1。19。3alpine
  然后使用curl模拟日常业务中的POST请求:curld{key1:value1,key2:value2}XPOSThttp:localhost:3000
  你将看到下面的返回结果:htmlheadtitle405NotAllowedtitleheadbodycenterh1405NotAllowedh1centerhrcenternginx1。19。3centerbodyhtml
  按图索骥,查看Nginx模块modulesngxhttpstubstatusmodule。c和httpngxhttpspecialresponse。c的源码可以看到下面的实现:staticngxinttngxhttpstubstatushandler(ngxhttprequesttr){sizetsize;ngxinttrc;ngxbuftb;ngxchaintout;ngxatomicinttap,hn,ac,rq,rd,wr,wa;if(!(rmethod(NGXHTTPGETNGXHTTPHEAD))){returnNGXHTTPNOTALLOWED;}。。。}。。。staticcharngxhttperror405page〔〕htmlCRLFheadtitle405NotAllowedtitleheadCRLFbodyCRLFcenterh1405NotAllowedh1centerCRLF;defineNGXHTTPOFF4XX(NGXHTTPLAST3XX301NGXHTTPOFF3XX)。。。ngxstring(ngxhttperror405page),ngxstring(ngxhttperror406page),。。。
  没错,默认情况下,NGINX并不支持记录POST请求,会根据RFC7231展示错误码405。所以一般情况下,我们会借助LuaJavaPHPGoNode等动态语言进行辅助解析。
  那么如何来解决这个问题呢?能否单纯的使用性能好、又轻量的Nginx来完成对POST请求的支持,而不借助外力吗?让Nginx原生支持POST请求
  为了更清晰的展示配置,我们接下来使用compose来启动Nginx进行实验,在编写脚本之前,我们需要先获取配置文件,使用下面的命令行将指定版本的Nginx的配置文件保存到当前目录中。dockerrunrmitnginx:1。19。3alpinecatetcnginxconf。ddefault。confdefault。conf
  默认的配置文件内容如下:server{listen80;servernamelocalhost;charsetkoi8r;accesslogvarlognginxhost。access。logmain;location{rootusrsharenginxhtml;indexindex。htmlindex。htm;}errorpage404404。html;redirectservererrorpagestothestaticpage50x。htmlerrorpage50050250350450x。html;location50x。html{rootusrsharenginxhtml;}proxythePHPscriptstoApachelisteningon127。0。0。1:80location。php{proxypasshttp:127。0。0。1;}passthePHPscriptstoFastCGIserverlisteningon127。0。0。1:9000location。php{roothtml;fastcgipass127。0。0。1:9000;fastcgiindexindex。php;fastcgiparamSCRIPTFILENAMEscriptsfastcgiscriptname;includefastcgiparams;}denyaccessto。htaccessfiles,ifApachesdocumentrootconcurswithnginxsonelocation。ht{denyall;}}
  稍作精简,我们会得到一个更简单的配置文件,并在其中添加一行errorpage405200uri;:server{listen80;servernamelocalhost;charsetutf8;location{return200soulteary;}errorpage405200uri;}
  将本小节开始部分的命令改写为dockercompose。yml并添加volumes,把刚刚导出的配置文件映射到容器内,方便使用后续使用compose启动容器进行验证。version:3services:ngx:image:nginx:1。19。3alpinerestart:alwaysports:3000:80volumes:。default。conf:etcnginxconf。ddefault。conf
  使用dockercomposeup启动服务,然后使用前面的curl模拟POST验证请求是否正常。curld{key1:value1,key2:value2}HContentType:applicationjsonHorigin:gray。baai。ac。cnXPOSThttp:localhost:3000soulteary
  执行完毕,除了得到soulteary这个字符串返回之外,Nginx日志记录也会多一条看起来正常的记录:ngx1192。168。16。1〔31Oct2020:14:24:480000〕POSTHTTP1。12000curl7。64。1
  但是,如果你细心的话,你会发现日志中并未包含我们发送的数据,那么这个问题该如何解决呢?解决Nginx日志中丢失的POST数据
  这个问题其实是老生常谈,默认Nginx服务器记录日志格式并不包含POSTBody(性能考虑),并且在没有proxypass的情况下,是不会解析POSTBody的。
  先执行下面的命令:dockerrunrmitnginx:1。19。3alpinecatetcnginxnginx。conf
  可以看到默认的logformat配置规则中确实并没有任何关于POSTBody中的数据。usernginx;workerprocessesauto;errorlogvarlognginxerror。logwarn;pidvarrunnginx。pid;events{workerconnections1024;}http{includeetcnginxmime。types;defaulttypeapplicationoctetstream;logformatmainremoteaddrremoteuser〔timelocal〕requeststatusbodybytessenthttprefererhttpuseragenthttpxforwardedfor;accesslogvarlognginxaccess。logmain;sendfileon;tcpnopushon;keepalivetimeout65;gzipon;includeetcnginxconf。d。conf;}
  所以解决这个问题的方案也不难,新增一个日志格式,添加POSTBody变量(requestbody),然后添加一个proxypass路径,激活Nginx解析POSTBody的处理逻辑。
  考虑到维护问题,我们前文中的配置文件与这个配置进行合并,并定义一个名为internalapipath的路径:usernginx;workerprocessesauto;errorlogvarlognginxerror。logwarn;pidvarrunnginx。pid;events{workerconnections1024;}http{includeetcnginxmime。types;defaulttypeapplicationoctetstream;logformatmainremoteaddrremoteuser〔timelocal〕requeststatusbodybytessenthttprefererhttpuseragenthttpxforwardedforrequestbody;accesslogvarlognginxaccess。logmain;sendfileon;keepalivetimeout65;server{listen80;servernamelocalhost;charsetutf8;location{proxypasshttp:127。0。0。1internalapipath;}locationinternalapipath{accesslogoff;defaulttypeapplicationjson;return200{code:0,data:soulteary};}errorpage405200uri;}}
  将新配置文件保存为nginx。conf后,调整compose中的volumes配置信息,再次使用dockercomposeup启动服务。volumes:。nginx。conf:etcnginxnginx。conf
  再次使用curl模拟之前的POST请求,会看到Nginx日志多了两条记录,第一条记录中包含了我们所需要的POST数据:192。168。192。1〔31Oct2020:15:05:480000〕POSTHTTP1。120029curl7。64。1{key1:value1,key2:value2}127。0。0。1〔31Oct2020:15:05:480000〕POSTinternalapipathHTTP1。020029curl7。64。1
  但是这里不完美的地方还有很多:服务器可以正常接收GET请求,我们在日志处理的时候需要进行大量抛弃动作,并且在暂存的时候,磁盘空间也存在不必要的浪费。用于激活NginxPOSTBody解析能力的路径可以被随意调用,产生无意义日志,同样存在上面的问题。更关键的,日志中的数据看起来还需要额外加工处理,进行转码,解析效率会有不必要的性能损耗。
  接下来我们来继续解决这些问题。改进Nginx配置,优化日志记录
  首先,在日志格式中添加escapejson参数,要求Nginx解析日志请求中的JSON数据:logformatmainescapejsonremoteaddrremoteuser〔timelocal〕requeststatusbodybytessenthttprefererhttpuseragenthttpxforwardedforrequestbody;
  然后,在不需要记录日志的路径中,添加accesslogoff;指令,避免不必要的日志进行记录。locationinternalapipath{accesslogoff;defaulttypeapplicationjson;return200{code:0,data:soulteary};}
  接着使用Nginxmap指令,和Nginx中的条件判断,过滤非POST请求的日志记录,以及拒绝处理非POST请求。maprequestmethodloggable{default0;POST1;}。。。server{location{if(requestmethod!POST){return405;}accesslogvarlognginxaccess。logmainifloggable;proxypasshttp:127。0。0。1internalapipath;}。。。}
  再次使用curl请求,会看到日志已经能够正常解析,不会出现两条日志了。192。168。224。1〔31Oct2020:15:19:590000〕POSTHTTP1。120029curl7。64。1{key1:value1,key2:value2}
  同时,也不会再记录任何非POST请求,使用POST请求的时候,会提示405错误状态。
  这个时候,你或许会好奇,为什么这个405和前文中不同,不会被重定向为200呢?这是因为这个405是我们根据触发条件手动设置的,而非Nginx逻辑运行过程中判断出新的结果。
  当前的Nginx配置如下:usernginx;workerprocessesauto;errorlogvarlognginxerror。logwarn;pidvarrunnginx。pid;events{workerconnections1024;}http{includeetcnginxmime。types;defaulttypeapplicationoctetstream;logformatmainescapejsonremoteaddrremoteuser〔timelocal〕requeststatusbodybytessenthttprefererhttpuseragenthttpxforwardedforrequestbody;sendfileon;keepalivetimeout65;maprequestmethodloggable{default0;POST1;}server{listen80;servernamelocalhost;charsetutf8;location{if(requestmethod!POST){return405;}accesslogvarlognginxaccess。logmainifloggable;proxypasshttp:127。0。0。1internalapipath;}locationinternalapipath{accesslogoff;defaulttypeapplicationjson;return200{code:0,data:soulteary};}errorpage405200uri;}}
  但是到这里就真的结束了吗?模拟前端客户端常见跨域请求
  我们打开熟悉的百度,在控制台中输入下面的代码,模拟一次常见的业务跨域请求。asyncfunctiontestCorsPost(url,data{}){constresponseawaitfetch(url,{method:POST,mode:cors,cache:nocache,credentials:sameorigin,headers:{ContentType:applicationjson},redirect:follow,referrerPolicy:noreferrer,body:JSON。stringify(data)});returnresponse。json();}testCorsPost(http:localhost:3000,{hello:soulteary})。then(dataconsole。log(data));
  代码执行完毕后,你会看到一个经典的提示信息:Accesstofetchathttp:localhost:3000fromoriginhttps:www。baidu。comhasbeenblockedbyCORSpolicy:Responsetopreflightrequestdoesntpassaccesscontrolcheck:NoAccessControlAllowOriginheaderispresentontherequestedresource。Ifanopaqueresponseservesyourneeds,settherequestsmodetonocorstofetchtheresourcewithCORSdisabled。POSThttp:localhost:3000net::ERRFAILED
  观察Network网络面板,会看到有两条失败的新请求:RequestURL:http:localhost:3000RequestMethod:OPTIONSStatusCode:405NotAllowedRequestURL:http:localhost:3000RequestMethod:POST没有响应结果
  让我们继续调整配置,解决这个常见的问题吧。使用Nginx解决前端跨域问题
  我们首先调整之前的过滤规则,允许OPTIONS请求的处理。if(requestmethod!(POSTOPTIONS)){return405;}
  跨域请求是前端常见场景,许多人会偷懒使用来解决问题,但是Chrome等现代浏览器在新版本中有些场景不能使用这样宽松的规则,而且为了业务安全,一般情况,我们会在服务端设置允许进行跨域请求的域名白名单,参考上文中的方式,我们可以很容易的定义出类似下面的Nginxmap配置,来谢绝所有前端非授权跨域请求:maphttporigincorsHost{default0;(。)。soulteary。com1;(。)。baidu。com1;}server{。。。location{。。。if(corsHost0){return405;}。。。}}
  这里有一个trick的地方,Nginx的路由内的规则编写,并不完全类似级编程语言一样,可以顺序执行,是具备优先级覆盖关系的,所以为了能够让前端正常调用接口进行数据提交,这里需要这样书写规则,存在四行代码冗余。if(corsHost0){return405;}if(corsHost1){不需要CookieaddheaderAccessControlAllowCredentialsfalse;addheaderAccessControlAllowHeadersAccept,Authorization,CacheControl,ContentType,DNT,IfModifiedSince,KeepAlive,Origin,UserAgent,XMxReqToken,XRequestedWith,Date,Pragma;addheaderAccessControlAllowMethodsPOST,OPTIONS;addheaderAccessControlAllowOriginhttporigin;}OPTION请求返回204,并去掉BODY响应,因NGINX限制,需要重复上面的前四行配置if(requestmethodOPTIONS){addheaderAccessControlAllowCredentialsfalse;addheaderAccessControlAllowHeadersAccept,Authorization,CacheControl,ContentType,DNT,IfModifiedSince,KeepAlive,Origin,UserAgent,XMxReqToken,XRequestedWith,Date,Pragma;addheaderAccessControlAllowMethodsPOST,OPTIONS;addheaderAccessControlAllowOriginhttporigin;addheaderAccessControlMaxAge1728000;addheaderContentTypetextplaincharsetUTF8;addheaderContentLength0;return204;}
  再次在网页中执行前面的JavaScript代码,会发现请求已经可以正常执行了,前端数据会返回:{code:0,data:soulteary}
  而Nginx日志,则会多一条符合预期的记录:172。20。0。1〔31Oct2020:15:49:170000〕POSTHTTP1。120031Mozilla5。0(Macintosh;IntelMacOSX10156)AppleWebKit537。36(KHTML,likeGecko)Chrome86。0。4240。111Safari537。36{hello:soulteary}
  而使用curl执行之前的命令,继续模拟纯接口调用,则会发现出现了405错误响应,这是因为我们的请求中不包含origin请求头,无法表明我们的来源身份,在请求中使用H参数补全这个数据,即可拿到符合预期的返回:curld{key1:value1,key2:value2}HContentType:applicationjsonHorigin:www。baidu。comXPOSThttp:localhost:3000{code:0,data:soulteary}相对完整的Nginx配置
  到现在为止,我们基本实现一般的采集功能,满足基本诉求的Nginx配置信息如下:usernginx;workerprocessesauto;errorlogvarlognginxerror。logwarn;pidvarrunnginx。pid;events{workerconnections1024;}http{includeetcnginxmime。types;defaulttypeapplicationoctetstream;logformatmainescapejsonremoteaddrremoteuser〔timelocal〕requeststatusbodybytessenthttprefererhttpuseragenthttpxforwardedforrequestbody;sendfileon;keepalivetimeout65;maprequestmethodloggable{default0;POST1;}maphttporigincorsHost{default0;(。)。soulteary。com1;(。)。baidu。com1;}server{listen80;servernamelocalhost;charsetutf8;location{if(requestmethod!(POSTOPTIONS)){return405;}accesslogvarlognginxaccess。logmainifloggable;if(corsHost0){return405;}if(corsHost1){不需要CookieaddheaderAccessControlAllowCredentialsfalse;addheaderAccessControlAllowHeadersAccept,Authorization,CacheControl,ContentType,DNT,IfModifiedSince,KeepAlive,Origin,UserAgent,XMxReqToken,XRequestedWith,Date,Pragma;addheaderAccessControlAllowMethodsPOST,OPTIONS;addheaderAccessControlAllowOriginhttporigin;}OPTION请求返回204,并去掉BODY响应,因NGINX限制,需要重复上面的前四行配置if(requestmethodOPTIONS){addheaderAccessControlAllowCredentialsfalse;addheaderAccessControlAllowHeadersAccept,Authorization,CacheControl,ContentType,DNT,IfModifiedSince,KeepAlive,Origin,UserAgent,XMxReqToken,XRequestedWith,Date,Pragma;addheaderAccessControlAllowMethodsPOST,OPTIONS;addheaderAccessControlAllowOriginhttporigin;addheaderAccessControlMaxAge1728000;addheaderContentTypetextplaincharsetUTF8;addheaderContentLength0;return204;}proxypasshttp:127。0。0。1internalapipath;}locationinternalapipath{accesslogoff;defaulttypeapplicationjson;return200{code:0,data:soulteary};}errorpage405200uri;}}
  如果我们结合容器使用,只需要在其中添加一段额外的路由定义,单独用于健康检查,就能够实现一个简单稳定的采集服务。继续对接后续的数据转存、处理程序。locationhealth{accesslogoff;return200;}
  而compose配置文件,相比较之前,不过多了几行健康检查定义罢了:version:3services:ngx:image:nginx:1。19。3alpinerestart:alwaysports:3000:80volumes:etclocaltime:etclocaltime:roetctimezone:etctimezone:ro。nginx。conf:etcnginxnginx。confhealthcheck:test:wgetspiderlocalhosthealthexit1interval:5stimeout:10sretries:3
  结合Traefik,可以轻松进行实例的水平扩展,处理更多的请求。感兴趣可以翻阅我之前的文章。最后
  本文仅介绍了数据采集的皮毛,更多的内容或许后续有时间会细细道来。要给我家毛孩子付猫粮尾款啦,先写到这里吧。

九不得会杀死社区团购吗?可能是移动互联网最后一场战役的社区团购,一只脚已经踏进当年直播答题同一条河里。据媒体报道,12月22日,国家市场监管总局联合商务部,组织召开了规范社区团购秩序行政指导会,……红米note11pro一个回归,四个超越,一个首创,千元机皇雷哥正式发文,表式10月28日将会发布新一代的小金刚,大家都知道红米等note系列一直都有小金刚的称号,完全是因为它那超高的性价比。红米note10Pro超高性价比已经让广大用……内网渗透常用工具免杀内网渗透常用工具免杀Mimikatz免杀Mimikatz其实并不只有抓取口令这个功能,它还能够创建票证、票证传递、hash传递、甚至伪造域管理凭证令牌等诸多功能。由于mi……用智慧治大城市病大数据筑起智慧城市大城市病需用智慧治智慧城市并非一个复杂的概念,它更多地体现在线上、线下的完美结合,简而言之就是通过互联网与城市中各行各业的结合缓解大城市病、提高当地居民的生活质量,同时大……办公大楼捡垃圾,帮老板组装省钱办公电脑,你就是办公室最靓的崽导读因为疫情原因,现在很多公司都开始收缩开支,老程今天就给一个上市公司维护机器,这家上市公司是做艾灸康复的,本来搬家的时候就联系我准备更换电脑的,但是因为上半年直接没有营……不笑你打我精选内涵段子1。带破洞的牛仔裤刚流行那年,邻村有个青年因打架伤人坐了两年牢出狱了,他老婆给他带来了一身新衣裤。当他换上了当时最流行的破洞牛仔裤后百感交集,抱着他老婆放声大……我奶奶亲手给我做了个套!哈哈哈哈哈哈哈哈哈哈都说每个老人随着渐渐苍老最终都会变回孩子他们变得越来越可爱越来越孩子气也越来越温暖。。。。。。78岁的奶奶,忽然迷上了制作各种毛线的小玩意儿。可是画风却越来越跑偏了。。。……运动与豪华并存,奥迪Q8即将于10月25日上市据悉继奥迪Q7之后,奥迪全新旗舰SUV奥迪Q8将于10月25日上市,预售价:77万102万奥迪Q8采用与兰博基尼Urus相同平台,该车尺寸为4986mmX1995mmX1……专家视点公域流量的尽头数字营销回归商业本质公域流量涸泽引发的思考如今,公域平台流量红利已然走到尽头,所谓存量运营,背后其实是行业巨头对流量垄断的割据格局基本已经形成,你的流量其实是在给他们打工。。。今天可以给你流……OPPO公布Reno十倍变焦样张手机摄影进入长焦时代2018年,智能手机行业发生了巨大的变化,各家都在探索全面屏的呈现方式以及关于摄影和快充的新技术。2019年,智能手机行业还是会聚焦摄影,而旗舰系列则会率先迈进长焦时代。……十年回顾!Find系列那些不得不说的神作你都知道吗?随着智能手机的发展,如今各大手机厂商都开始发力高端旗舰,而不少手机大厂更是从多年前就开始深耕这一领域,打造了专属于自己的高端旗舰品牌。以OPPO为例,自2011年推出了主打高端……纽约梅隆银行宣布将支持加密交易平台PureDigital纽约梅隆银行公司宣布,它将与道富公司和其他四家银行一起支持位于伦敦的加密货币交易平台PureDigital。根据《金融时报》的报道,此举表明,纽约梅隆和道富这两家世界上最……
千元降噪耳机大比拼华为1MoreSony谁最值得买?有人在某乎上提了个问题:1More降噪豆,华为FreeBudsPro及SonyWF1000XM3哪个耳机最值得买?我正好放假最后一天在家无事,就回答一下,顺便搬过来给大家……国庆回家吃不习惯?聪明人知道给父母带田螺云厨盼星星盼月亮,国庆七天小长假终于在赶来的路上。这不仅意味着我们可以随意睡到自然醒,还能尽情地在家胡吃海喝。但也有一些年轻人习惯了大城市天南海北的美食,喜欢吃的饭菜甚至是父母都没……太原天龙山,窑头村之行太原天龙山,网红公路,这些大家都应该耳熟能详了,但是从去年秋季开始,天龙山就进入了封山状态,今年又加上疫情的影响,直至6。1才恢复开放,当然,这段时间山上也没有闲着,正在修建各……2021成都车展实拍比亚迪汉国漫版2021成都车展上,各大车企共计带来150多款新车型,其中有很多车型是首次亮相,但在众多车型中,上市已经一年有余的比亚迪汉依旧是本次车展的焦点,其中的国漫版尽显中国风采。……车子的加速度究竟是由扭矩决定,还是由功率决定?不知从何时开始,汽车圈中刮起了一阵关于扭矩大小决定加速度的风,这阵风持续了很久;似乎消费者购车一定要看扭矩的大小,至于功率之类的参数统统不重要,但实际上这种扭矩大小决定加速度的……新手入门网站建设,为什么建议选虚拟主机?【新网虚拟主机资讯】随着互联网发展日趋深入,云计算、大数据技术快速更新,云虚拟主机也普及到各行各业中。如果你做过网站,那么你肯定知道选择一台合适的、稳定的虚拟主机或者云服务器,……环球影城收购品牌域名Universal。com【新网域名资讯】今年5月,继好莱坞(1964年开园)、奥兰多、大阪(2001年开园)和新加坡后,全球第5家环球影城北京环球影城即将开业。说起环球影城,可能大家不熟悉,但说起迪士……不用好运气也可以躺赢,余额宝1天帮赚1个亿今年,是我们见证许多梦想实现的一年,广大网友的梦想不仅要追求赢,还要追求躺赢。比如做女团里的杨超越,锦鲤里的信小呆。然鹅,咸鱼毕竟不好翻身,不是所有人都有主角光环傍身,也……一加8银翼用户评价汇总男女通吃,估计刘作虎都没料到一加8系列正式发布之前,刘作虎曾微博表示:今年会有专门针对女性用户的新配色。一加手机诞生于6年前,因为初期在美国硅谷受到了众多极客的高度认可和追捧,多年来一直背负着直男专……旗舰射门员VS旗舰守门员,reamle真我GTNeo放大招一直以来,手机行业的竞争最为激烈。今年三月,发布了不少中高端旗舰产品,比如,已被定义旗舰守门员的红米K40系列,还有旗舰双芯的iQOONeo5,亮点都有很多。而在3月31日,还……每次10分钟跟我学Python(第五十七次课)大家好!我是幻化意识流。今天继续跟我学Python。上次课,我给大家发了一封密文电报,不知道是否有同学已经解密了。现在就来公布一下密文电报的内容:开始编写一个Uni……剪辑创作没有趁手的工具?游戏本太厚实不方便携带?Ta解你痛点前言笔记本电脑,对于职场达人、刚进入大学校园的学生、软件工程师、剪辑创作者、自媒体人等人群可以说是刚需的生产力工具,作为便携、高效的办公利器,它几乎是人手一台。在我……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网