fastrpc- 高性能跨平台c++协程rpc框架

网友投稿 1304 2022-10-12

fastrpc- 高性能跨平台c++协程rpc框架

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

        <%=title%>                    <% for item in data{ %>            <%=item["id"]%>,<%= item["name"] %>            array data:            <% for i in item["array"] {%><%= i %><%}%>            
            <%}%>            

则对应的使用

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小时内删除侵权内容。

上一篇:PMP-6.项目进度管理-6.4估算活动持续时间
下一篇:public static String getProcessName(Context context) {
相关文章

 发表评论

暂时没有评论,来抢沙发吧~