洞察管理小程序实例的关键在于实现跨平台能力与数据安全,如何利用FinClip助力企业在数字化转型中既合规又高效?
1304
2022-10-12
fastrpc- 高性能跨平台c++协程rpc框架
用过go erlang gevent的亲们应该都会知道协程在应用中带来的方便。
如果对协程不理解的同学,通过阅读下面例子可以快速了解我们框架的协程的意义,已了解的可以跳过这部分。
协程例子 :假设我们要发个Get请求获取百度首页内容;
php同步方式:$result =file_get_contents(“http://baidu.com”),php果然是世界上最好的语言,多么简洁。
然后java和c++的同学开始不屑了: “呵呵, 同步,鄙视你不解释。”
好了那就异步吧,php也有异步的方式啦,例如使用腾讯的swoole框架,
get('ww.baidu.com', function ($cli) { echo $cli->body;});
执行到get请求时,当前进程不阻塞等待,可以继续去处理后面的事情,等到响应结果返回再去执行回调function里的代码。
这样是不是就完美了呢,我们来看看异步的问题:
这里这个代码被拆分成两个块调用$cli->get函数之前块和回调函数里面的块,问题就出在这里,现在假设有一个需求,我要给发Get请求之前的一个html变量追加百度返回的内容,那么我必须这么做:
html = $html;$cli->get('w.baidu.com', function ($cli) { echo $cli->html.$cli->body;});
也就是说get之前和回调函数里是两个不同的代码区域,要在两个不同代码区域连续使用一个局部变量,那么我们的做法就是必须传值(传值有很多种方式了,这里不一一列举).
也许有的同学会说就传个值而已嘛,也没什么不好的呀。好那么我们来看看一段实际项目中的代码(不用真的去看懂):
//代码块1$url = $_SERVER['HTTP_HOST'] . $_SERVER["REQUEST_URI"];$this->history_add( $wid, $sku_id, 2 );$smarty = get_smarty();$sku = new Sku( $sku_id );$goods = new Goods( $sku->goods_id );$smarty->assign('pinlei_name', $goods->goods_name );$smarty->assign("title_name", PageListWorksItem::trans_goods_name( $goods->goods_name ));$this->goods_id = $goods->goods_id;$work = new Works( $wid );$work_data = $work->get_data();$s = new ImgArgs( $work_data );//插入点 在这里插入获取百度首页内容//代码块2$merge_img_url = $s->get_product_merge_img_url( $sku_id , 2 );$work_data['img_url'] = $merge_img_url;$work_data['real_price'] = $work_data['money'] + $sku->price;// $reply_count = WorksComment::get_works_comment_list_count2( $sku->goods_id );$reply_count = WorksComment::get_works_comment_list_count( $sku->goods_id );$smarty->assign('reply_count', $reply_count );...后面代码省略,其中以上所有对象和变量在后续代码中都使用到
在上面插入点这个地方 我们需要调用异步发请求去获取百度首页内容,然后在代码块2中使用到百度的响应结果,
这样我们就要把代码块2这部分全部迁移到回调函数中,接着修改的同学就要抓狂了,要把代码块1中一个一个变量去检查,看后续是否用到,然后一个一个去传值给回调函数。
同理,在中间插入点,我们要做其它操作,例如查mysql 查redis时,同样会出现上述 同步和异步的选择困难问题。
好,到这里使用js的同学表示不服了,这有什么难,看我大js:
function asynGetBaidu(){ var url = "xxx"; var html = "init"; $.get(url, {}, function(data,status) { alert(html+data); });}
你看,不是不需要传值在回调内也可以使用外部变量吗?
这是因为,js在底层闭包的自动传值机制,把asynGetBaidu函数内的变量都可以在回调函数内作用。
那么问题又来了,如果原来函数是有返回值的,我们在这加了异步get后返回值还能有效吗?
function asynGetBaidu(){ var url = "xxx"; var html = "init"; $.get(url, {}, function(data,status) { html = html+data; return html; });}var res = asynGetBaidu();alert(res);
弹出结果是undefine。有人又说了,真无聊,网页端可以通过全局变量,或网页标签取返回值就行了,这里返回值没什么意思啦。
是的对网页端来说jquery功能完全够用了,但是对服务器转发请求来说,我们又能设多少个这样的全局变量呢?(最新的es6es7已经支持协程新特性,可查询koa的 yield 或 async/await使用)
那怎么办呢?我们假设场景是这样: 在服务器然后端 processer函数用来处理客户端来的http请求,然后再转向百度获取信息,打印返回给客户端。
来看看经过我们封装后python的实现(人生苦短,我用python):
import gevent.monkeygevent.monkey.patch_socket()def asynGetBaidu(): html = "init" ... f=urllib.urlopen("ww.baidu.com") ... return html + f.read()def processer(self, request, response): response = asynGetBaidu() print response
坑谁呢,这明明就是同步发GET请求。
没错这就是协程的作用了,同步的编码方式,异步的效果。上述代码要在我们fastpy框架下使用才会真的起到异步的效果。
每当有一个http请求到来时,框架会新开一个协程,这个协程执行processer函数,当执行到urllib.urlopen时,因为有io阻塞会主动放弃对cpu的使用权,让给谁呢?让给下一个协程,下个协程又会执行processer去处理下一个http请求。
等到baidu有响应结果了,框架引擎会恢复之前暂停的协程,继续执行processer内剩余的代码,这样我们想在asynGetBaidu中插入多少段请求别的服务的代码都只需要像同步一样编写,不需要调整上下文代码结构。
试想想,urlopen处如果是mysql query或redis的get,都可以0代价的同步自动转换为异步,多么方便的一件事啊。
说到底,我们其实就是在抄go语言的特性啦,而且抄的远远不如那样, 是不是感觉我们在为go做广告。
有人会说了,go语言那么好,那你们为何不用啊。—回答:在国内不好找工作啊。
言归正传,看看我们团队c++和python的两个协程解决方案:
python解决方案: fastpy -————本框架是在gevent基础上封装而成,主要面向web应用:
源代码只有800多行 项目地址: https://git.oschina-/feimat/fastpy
性能比较如下
tornado 4kqps 多进程1wqpsnginx+tornado 9kqpsnginx+uwsgi 8kqps
django和webpy 原生性能较差
本server 2w qps欢迎加入qq群339711102,一起探讨优化哦
快速入门:
1、启动:指定监听端口即可启动python fastpy.py 8992(如果需要使用gevent协程功能请先安装gevent,参考链接http://xue163.com/exploit/138/1381297.html)
2、快速编写cgi,支持运行时修改,无需重启server
在fastpy.py同一目录下随便建一个python 文件例如:example.py:
#-*- coding:utf-8 -*- import sys #定义一个同名example类 #定义一个tt函数: reload(sys) sys.setdefaultencoding('utf8') FastpyAutoUpdate=True class example(): def tt(self, request, response_head): #print request.form #print request.getdic #fileitem = request.filedic["upload_file"] #fileitem.filename #fileitem.file.read() return "ccb"+request.path
则访问该函数的url为 http://ip:port/example.tt协程的使用上面已经演示过这里不再重复,详情请看代码示例cgi所有使用例子:sample.py 上传文件和form表单使用等基本api的例子example.py 使用单例模式和线程+异步返回的例子WithGevent/dbsample.py 使用gevent+pymysql实现异步读写数据库的例子(gevent下线程池实现)WithGevent/sample.py 使用gevent实现异步发送http请求的例子sendfile/sendfile.py 多线程文件上传服务端代码sendfile/sendfile_client.py 多线程文件上传客户端代码proxy_server/proxy.py 正向代理服务器代码跨平台/ 跨平台版本的fastpy.py
3、支持超大文件上传-默认静态文件(包括html,js、css、图片、文件等)放在static文件夹下html和js、css会自动压缩加速例如把a.jpg放到static文件夹下访问的url为 http://ip:port/static/a.jpg支持etag 客户端缓存功能(server 使用sendfile进行文件发送,不占内存且快速)
4、支持网页模板编写模版引擎代码只有十几行 在WithGevent/common/core.py 文件里的class FeimaTpl模版用法很简单1、用于写python代码的控制块 <% for item in data { %><% %> 中间支持python的 if else for while等程序控制块,不同是模版用{ }来代替python 晦涩的缩进来区分控制块范围2、取值块 <%=item[“id”]><%= %> 里写的是python的变量指即可,可在1中控制块内使用
下面看个例子创建一个模板 a.html
则对应的使用
from common import core tpl = core.FeimaTpl(filepath="./a.html") d = [] d.append({"id":1,"name":"name1","array":[2,4,5,6]}) d.append({"id":2,"name":"name2","array":[1,3,5,7,9]}) tpl.assign("title", "my title") tpl.assign("data", d) print tpl.render()
则生成:
my title
1,name1
array data:
2456
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~