V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
NxnXgpuPSfsIT
V2EX  ›  Python

Trip: 协程 Requests 实战,获取免费代理

  •  3
     
  •   NxnXgpuPSfsIT ·
    littlecodersh · 2017-12-14 16:42:13 +08:00 · 6481 次点击
    这是一个创建于 2539 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本文使用开源协程网络库 Trip,解决验证代理时耗时的问题。

    简介

    验证大量的网上代理一直是爬虫很麻烦的一个工作。 例如我要发出十万份请求,十个请求一个代理,每个代理验证的延时五秒钟。 那么需要的时间为:

    100000 / 10 * 5 = 50000

    这就是半天时间了,当然,我们可以开很多的线程。 这里我们开二十个线程运行这样的验证,那么需要的时间为:

    100000 / 10 * 5 / 20 = 2500

    2500 秒即四十分钟多,不包括抓取和处理,仍旧要消耗大量的时间。

    而换用了协程框架后,你可以同时验证大量代理。 即使在一次验证中仅验证一百个代理,需要的时间也会变为:

    100000 / 10 * 5 / 100 = 500

    十万请求需要的代理在九分钟内就可以完成验证,这还仅是一个线程。 如果开多个线程同时处理还可以达到更快的速度。

    另外,等待代理返回请求的过程当中完全不影响处理别的内容。 例如,这一个线程在验证的八分钟内完全可以继续充当公众号服务器继续回复消息,完全不会影响。

    开始之前

    本文基于依赖库 Trip,请保证更新至最新版本:

    python -m pip install trip -U
    

    另外,本文涉及协程,建议使用之前基本了解协程是什么,可以写出一个合格的协程程序。

    Trip 兼容 Python 2、3,所以建议使用同样兼容的 Tornado 进行入门了解。

    具体实现

    由于 Trip 想要在协程的基础上充分还原 Requests 的操作,所以就有一个很简单的转换方式。

    先用 Requests 的写法写好,之后将所有的网络连接操作加上yield,就基本没有问题了。

    所以,下面的代码应该都不难理解。

    获取代理

    现在进入正文,首先是一个仅方便测试的代码段。

    由于大家都有自己的获取代理的方式,网上免费的代理也多如牛毛,我这里随便就挑了一个做测试:

    import re
    
    import trip
    
    def get_proxies(number=10):
        r = yield trip.get('http://www.89ip.cn/apijk/' +
            '?&tqsl=%s&sxa=&sxb=&tta=&ports=&ktip=&cf=1' % number)
        p = re.findall('((?:\d{1,3}.){3}\d{1,3}:\d+)', r.text)
        print(p)
    
    trip.run(get_proxies)
    

    这段代码没什么要说的,89ip是我一分钟前搜来的网站,之后使用正则从中提取代理地址。

    验证代理

    代理的验证用一个很简单的思路:

    • 请求httpbin.org,根据是否成功返回判断代理是否可用
    • 当然实际使用过程当中可以直接请求目标站点
    • 清除返回内容为空的代理

    所以,这个思路下代码是这样的:

    from functools import partial
    import trip
    
    def test_proxy(proxy):
        try:
            r = yield trip.get('http://httpbin.org/get', timeout=5,
                proxies={ 'http': proxy, 'https': proxy })
            if 'httpbin.org' not in r.text:
                raise Exception('Invalid reply')
        except Exception as e:
            print('[%s]: FAIL %s' % (proxy, e))
        else:
            print('[%s]: SUCC %s' % (proxy, r.elapsed))
            raise trip.Return(proxy)
    
    trip.run(partial(test_proxy, '58.251.249.152:8118'))
    

    我们这里用了五秒的延时,五秒内没有反应的代理就舍弃。 当然你可以根据自己的需求改为十秒、十五秒。

    运行后你就会发现该代理完成了验证,显示了是否验证成功。

    你可能会问,这个代码的确跑了五秒钟,和普通的代码有什么区别呢?

    区别在于,有了协程,一个线程在这五秒钟里可以跑成百上千个这段代码,详见下面一节。

    充分利用这五秒钟

    如果你使用过 Tornado 或者一些其他的协程框架一定很熟悉这个操作:

    r = yield [future1, future2, future3]
    

    没错,这样三个操作就同时跑了起来 (我知道这不严谨,但看出这不严谨的也不需要我给你夸耀协程的好处了吧)。

    所以最简单的,我们的主程序可以这样写:(这段代码不能单独跑,注意)

    def main():
        proxies = yield get_proxies(100)
        r = yield [test_proxy(p) for p in proxies]
        print(filter(lambda x: x, r))
    

    第一句话获取 ip,第二句话让他们同时检测,第三句话过滤结果。

    完整的代码

    所以完整的代码是这样的(这段可以跑了,请随意):

    import re, time
    
    import requests, trip
    
    @trip.coroutine
    def get_proxies(number=10):
        r = yield trip.get('http://www.89ip.cn/apijk/' +
            '?&tqsl=%s&sxa=&sxb=&tta=&ports=&ktip=&cf=1' % number)
        p = re.findall('((?:\d{1,3}.){3}\d{1,3}:\d+)', r.text)
        raise trip.Return(p)
    
    @trip.coroutine
    def test_proxy(proxy):
        try:
            r = yield trip.get('http://httpbin.org/get', timeout=5,
                proxies={ 'http': proxy, 'https': proxy })
            if 'httpbin.org' not in r.text:
                raise Exception('Invalid reply')
        except Exception as e:
            pass
        else:
            raise trip.Return(proxy)
    
    def main():
        proxies = yield get_proxies(100)
        r = yield [test_proxy(p) for p in proxies]
        print(filter(lambda x: x, r))
    
    start_time = time.time()
    trip.run(main)
    print(time.time() - start_time)
    

    你可以运行一下,还是五秒钟,一百个代理的检测完成了。

    之后的话

    这里用了最简单的一种主程序的方法,你当然也可以使用更高级些的:

    @trip.coroutine
    def worker(wl, pl):
        while wl:
            p = wl.pop()
            r = yield test_proxy(p)
            if r:
                pl.append(r)
    
    def main():
        waiting_list, proxies_list = yield get_proxies(100), []
        yield [worker(waiting_list, proxies_list) for i in range(10)]
        print(proxies_list)
    

    这就看你喜欢了。

    你如果使用的是 Windows 系统,你可能会发现没法同时进行很多的请求。 这是很正常的问题,Windows 系统使用的是 select,没法同时处理很多请求。

    另外,剩下来的抓取应该就不需要我多说了。 Requests 怎么抓,这里也怎么抓,Session、timeout 什么的也全都支持。

    最后,有什么我没讲清楚的、想法、建议都可以与我邮件联系或者在文末回复。

    望本文能够有所帮助!

    29 条回复    2017-12-20 11:47:50 +08:00
    GreatMartial
        1
    GreatMartial  
       2017-12-14 16:48:16 +08:00 via Android
    大佬,好久不见。😄
    NxnXgpuPSfsIT
        2
    NxnXgpuPSfsIT  
    OP
       2017-12-14 17:02:47 +08:00
    @GreatMartial 没有没有,离大佬还差的远
    2225377fjs
        3
    2225377fjs  
       2017-12-14 17:07:32 +08:00
    实在看不懂这个意义何在,代码用法写起来也不是很干净。
    gevent 的 patch 之后,requests 直接拿来用就好了。。API 更简洁,性能也很好。
    NxnXgpuPSfsIT
        4
    NxnXgpuPSfsIT  
    OP
       2017-12-14 17:46:30 +08:00
    @2225377fjs
    这就是最基本的协程代码,不干净的点在哪里?
    和 Requests 完全相同的操作,而协程代码就是有 yield 和 yield from 的。
    gevent 加 patch 永远也不知道会出什么问题,怎么和别的协程框架配合,你可以网上搜一下相关内容的吐槽。
    owenliang
        5
    owenliang  
       2017-12-14 17:48:04 +08:00
    python 协程的确烧脑,之前看了看 asyncio 的 API,惨不忍睹。
    2225377fjs
        6
    2225377fjs  
       2017-12-14 17:57:44 +08:00
    @NxnXgpuPSfsIT 呵呵了,各种关键字修饰,这种看起来比写起来还蛋疼的东西有何用,gevent 本身就是一个协程框架,为啥还要与别的协程框架配合? patch 之后可以兼容绝大多数 python 的库,什么 Python 环境下别的协程框架能实现?
    有人吐槽说明有人用啊。
    有问题不应该首先要解决问题么,难道你认为这半吊子框架能比的上 Gevent ?


    “而协程代码就是有 yield 和 yield from 的”。。。呵呵,呵呵,这是你定义的么。?
    NxnXgpuPSfsIT
        7
    NxnXgpuPSfsIT  
    OP
       2017-12-14 18:19:47 +08:00
    @2225377fjs
    首先是这样的,我能理解你对于 gevent 的喜爱,我也认可这是一个很好的框架。我无意冒犯也无意和你争吵,只是和你交流一下可能的优劣。以及毕竟自己的库也写了一些时间,和你阐述一下我觉得他还是有一点用的地方。
    关于我说协程代码就是有 yield 和 yield from,我们可以看一下 pep380 和 pep492,我理解这可能很丑,但这就是除 gevent 之外的绝大部分协程,也是 python 官方给出的协程方案。
    毕竟 python 的协程是基于生成器产生程序顺序切换的,免不了加关键字。
    你觉得装饰器特别丑我也理解,Trip 如果使用 python3 就可以不写任何装饰器,python2 没有 async 关键字,为了给 python2 提供兼容我只能这样,目前我也没有改进的办法,找到我肯定第一时间换个好看的写法。你有什么想法的话可以给我一些指导。
    gevent 的话,我说的和别的协程框架配合是这个意思:python 最大的优势之一就是库多,我做什么就有什么库。目前已经有很多协程的库,大部分都是基于基本的增加关键词的协程框架,之后必然会更多。如果 gevent 没法和这些框架很好的兼容,可以想象之后会比其他协程框架少大量的可用库。丧失 python 一个巨大的优势。
    关于和 gevent 比,我比不上呀。我只是一个空下来把一些想法试着写出来的普通程序员,能有用最好。是不敢把自己放在大佬的位置上的,水平在那里,我自己知道。
    wcsjtu
        8
    wcsjtu  
       2017-12-14 18:32:21 +08:00
    py2 yield 和 return 不能共用就是蛋疼啊~ 用 raise 返回各种违和感
    NxnXgpuPSfsIT
        9
    NxnXgpuPSfsIT  
    OP
       2017-12-14 20:35:51 +08:00 via iPhone
    @wcsjtu 是的,py3 用这个库就可以 async 和 return 了
    所以我自己在用不是在写教程的时候也都是 3 的写法
    2225377fjs
        10
    2225377fjs  
       2017-12-14 20:45:00 +08:00   ❤️ 1
    @NxnXgpuPSfsIT 既然你回答了这么多,那我也得回一下才行啦,

    gevent 最大的一个优势就是可以与很多现有库兼容,api 也非常干净,性能也是 python 环境下最好的了,到你这里却变成了一个劣势,明明 gevent 自己就是一个协程库却还要去兼容别的协程库,这是什么逻辑?

    另外 async 就不丑了?看看 go 的设计,与 gevent 的思路非常相似,这才是好用的方案,有优雅的实现不用,要用蹩脚的实现,也是醉了。

    大家都是普通程序员,不更应该去学习掌握好用开源库,优点被你说成缺点,你确定你很了解 gevent,都还没有了解掌握就说这个东西的缺点是啥,然后去造一个自己心目中好用的轮子,这种思路不对吧?
    难道普通就要去造轮子?造的轮子还不好用。

    既然你说这框架的优点是与别的库配合起来方便,那么我就问一个问题,如何在这个库的协程并发环境下方便的访问 mongodb,访问 mysql,访问 redis,访问 kafka,rabbitmq,zookeeper 等常用的外部系统?既然都是协程了,那么肯定是要同步的 API 方式吧。我看好像不方便吧,那你再去看看 gevent 的环境下这些功能是怎么实现的?

    这个问题解决了就更容易回答这两个库谁的实用性更好了吧。

    最后,协程并发最大的优势是效率非常高,所以才能在很多环境下碾压线程并发,但是 Python 生成器翔一样的性能,估计也就只剩下了包装同步访问这一个作用了吧。
    NxnXgpuPSfsIT
        11
    NxnXgpuPSfsIT  
    OP
       2017-12-15 04:40:20 +08:00   ❤️ 2
    @2225377fjs
    你认为 gevent 是优雅的高效的产物,而我想告诉你实际上他只是一个没有办法的将就。

    首先,关于讨论,
    技术或者项目经验存在高低,是否能意识到 gevent 存在的问题都无所谓,我们可以慢慢交流。
    但是你希望还是可以认识到没有进行充分的论证,指责他人的工作毫无意义是一件很没有礼貌的事情。
    另外,措辞体现一个人的素质,尊重这件事情也是相互的,于人于己应该都是有利的。


    说他是将就,是因为:
    * gevent 会给项目带来毁灭性的隐患,且无法预料
    * 不区分协程函数和普通函数(不使用 yield )是一个很糟糕的习惯
    * 侵入式的兼容只能产生一个不可知的过渡用品,而不是一个有保障的产品

    所以需要非基于 gevent 的重新实现:
    * 他不会给程序埋下无法发现的隐患
    * 他使用了更好的协程习惯
    * 他每一个方法都有保障,不会充满了不可知


    我的观点里有一个大局,一个细节。


    说大局,
    主要是希望你把我放在一个同等的位置上听一下我说的话,讨论才能有意义。
    把我当成一个一无所知的跳梁小丑的话,可能你也提不起兴致和我交流。
    当然,如果你可以拿出你的项目让我认同,我也不介意自己以跳梁小丑的身份虚心求教。

    以我浅薄的见识看到的大局里,我不是一个人固执己见倾向于使用 yield 等关键字。
    和你讲一下我看到的大局,那些大牛都是怎么看待 gevent 的。

    Python 之父明确表明过 gevent 不是一个好的实现
    你可以在他的 Twitter 里看到: https://twitter.com/gvanrossum/status/443093650533142528
    gevent 早已出现,而加入标准库的是 asyncio 而非 gevent 更是对这一态度的明确实践。

    Requests 的作者对于本类项目的期待是我写这个项目的初衷。
    Requests 的作者一直认为使用 gevent 不是一个好的做法,他多次在 grequests 的 issue 中提出希望有基于 Future 和 yield 语法的协程 Requests。
    例如 Issue#10 和 Issue#51,他提出这是一个好的想法,但是他没有时间实现。
    ( Issue#10: suggestion: use concurrent.futures instead of gevent 中他这样说道:
    I'm 100% for this now — i've been talking about it for a few months now. Just need to implement ;))
    在 gevent 和其他的基于 yield 的协程的选择中,他百分百的站在了 gevent 的对立面。

    不说国外,国内学习 Python 多多少少听说过董伟明,特别是 Web 开发一块。
    他在个人的博客中明确表明:所以建议大家放弃 Gevent,拥抱 asyncio。
    http://www.dongwm.com/archives/使用 Python 进行并发编程-我为什么不喜欢 Gevent/)


    说细节,
    是希望我们可以在讨论中把优劣讨论的越来越清楚。
    我就回复开始的三个点一个一个解释:

    关于隐患,先上一个特别简单的代码:
    import multiprocessing, grequests; manager = multiprocessing.Manager()
    他报错了,如果不是这段代码非常简单的只有一个引用,你需要多久要怎么发现是 gevent 的问题?
    另外,这个问题你需要怎么修复?
    所以就有了很多报出 gevent 毁了他整个项目的情况: https://github.com/kennethreitz/grequests/issues/55,https://github.com/pytest-dev/pytest-xdist/issues/254
    不仅如此,关于垃圾回收,gevent 也会导致问题: https://www.projectcalico.org/the-sharp-edges-of-gevent/
    更不要说一些操作的误区,例如使用 RLock,却发现明明都是主线程却没法锁两次。

    使用 gevent 你难免会陷入是不是 gevent 又出问题了和完全没有修理的头绪的噩梦当中。
    所以我说 gevent 是一颗定时炸弹,而我的项目不是,所以我认为我的项目有意义。

    关于习惯,
    董伟明关于习惯有一段话说得很好,我就不画蛇添足了:
    Gvanrossum 说用它就是” patch-and-pray ”,太形象了。
    由于 Gevent 直接修改标准库里面大部分的阻塞式系统调用,包括 socket、ssl、threading 和 select 等模块,而变为协作式运行。
    但是我们无法保证你在复杂的生产环境中有哪些地方使用这些标准库会由于打了补丁而出现奇怪的问题,那么你只能祈祷( pray )了。
    在 Python 之禅中明确说过:「 Explicit is better than implicit.」,猴子补丁明显的背离了这个原则。
    我认可这个观点,即使仅是为了服务持有这个观点的这些人,我也认为我的项目有意义。

    关于三方库,
    你说 gevent 最大的一个优势就是可以与很多现有库兼容,api 也非常干净,我同意。
    gevent 通过侵入式的修改让一些三方库直接可用,这很棒。
    但这只是不可预知的部分可用,例如 Requests,你了解哪些部分是无法像预期那样表现的吗?
    另外,这些部分哪些是可以修复的,哪些是框架所限无法修复的呢?
    所以才需要一个让 Requests 所有部分功能都明确可用的库才出现。
    你提到了很多外部系统,本项目基于 tornado,你可以将这些和 tornado 或者 asyncio 进行配对搜索,你就能发现全都有相应支持。
    反观 gevent,kafka 的支持我并不认可 Github 上那个仅有 11 Star 长久未更新的项目,rabbitmq、zookeeper 之流更是不堪。
    我不满足于不可预知的部分可用,所以我认为这个项目有意义。

    当然还有一点是效率,
    你并没有证明你所提出的相对于其他协程解决方案的高效,这里提一下,希望可以在后面的交流中看到。
    另外,在证明的时候还是希望可以考虑到 gevent 对于 Python 解释器的限制。


    最后,感谢你的时间,也期待你的回复!
    2225377fjs
        12
    2225377fjs  
       2017-12-15 09:11:31 +08:00
    所以需要非基于 gevent 的重新实现:
    * 他不会给程序埋下无法发现的隐患
    * 他使用了更好的协程习惯
    * 他每一个方法都有保障,不会充满了不可知

    这都是你自己说的吧。。。。。

    引经据典这么多,看起来你不是读书太多了就是读书太少了,好多我看还是直接 copy 网上的文章吧,这么快餐。

    看起来你就是这个库的作者吧,也是呵呵了。。。

    “反观 gevent,kafka 的支持我并不认可 Github 上那个仅有 11 Star 长久未更新的项目,rabbitmq、zookeeper 之流更是不堪。 “

    你知道用的是哪个库么?自己都不清楚就开始胡说,还更是“不堪”,一会说自己是普通程序员,一会又自以为是的指点江山。

    还要跟你证明其他 Python 其他协程方案效率不行,你自己去证明吧。

    redis,mongodb,rabbitmq,zookeeper,etcd,requests,mysql 这些系统的相关库我们在生产环境跑的好好的,还有不少服务节点是跑在 pypy 下的,最开始想喷的就是,不要不了解情况就开始说人家这不好,那也不好,然后自己撸一个自以为是的半吊子的框架出来,Node.js 环境下就是这种太多了,各种各样的半吊子框架。

    普通程序员,既要谈大局,还要谈细节,你让我很不懂啊。
    NxnXgpuPSfsIT
        13
    NxnXgpuPSfsIT  
    OP
       2017-12-15 09:53:32 +08:00
    @2225377fjs
    换言之,你的回复告诉了我你不能就 gevent 的缺陷做出任何解释,也无法证明你唯一提出的观点高效。

    你认为 gevent 是优雅的高效的产物,而我告诉了你实际上他只是一个没有办法的将就。
    所以需要非基于 gevent 的重新实现:
    * 他不会给程序埋下无法发现的隐患
    * 他使用了更好的协程习惯
    * 他每一个方法都有保障,不会充满了不可知

    我说的,我证明了。

    而你空无一物的嚣张回答,相较于想要好好交流的同行,更倾向于一个无脑的喷子。

    我不想引战,所以各有优劣的观点我也每一个好好阐述。
    Python 之父,Requests 作者,董伟明的观点我摆出来了。
    可能产生的问题的代码,issue,原理我也摆出来了。

    而不像你,
    说都有相应支持,却一个也举不出来,告诉我你指的是什么? kafka、rabbitmq、zookeeper,我洗耳恭听。
    说 gevent 性能也是 python 环境下最好的,谁提出谁举证的道理不会不懂吧,没有举证就不要乱说。

    你说:“不要不了解情况就开始说人家这不好,那也不好”,送还给你。

    你不了解 yield, yield from,我把 pep 找给你看。
    你看不到 gevent 也存在自身巨大的缺陷,我找大牛找代码找原理和你讲。

    我的所有开源项目都在 github 上,你才知道我是这个库的作者吗?
    大家都是普通程序员所以我从来没有因为我写过几个项目就觉得自己很了解什么。
    而你随意指责别人的项目是半吊子,不拿出实际的理由也不给出足以信服的个人背景。
    不好意思,我不能接受。

    所以可以麻烦把你自信的来源向我展示一下吗?
    让我看看你到底写过什么。
    如果你想继续讨论而非无脑喷,请好好证明一下你的观点。
    xin053
        14
    xin053  
       2017-12-15 10:12:44 +08:00   ❤️ 1
    上面那位兄弟,请尊重别人,作为一个旁观者,明显可以感受到人家心平气和的跟你讨论,而你满口火药味。

    最开始关注这个作者是使用他的微信个人公众号库,后来又去研究他的微信公众号库(顺便吐槽一下,github 上该库几个 PR 都木有处理,虽然看到源码有些不太正确的写法,不过也没提 PR,想着作者应该没时间来维护这个也就算了)

    大概在二月前的时候我就开始研究 python 协程这一块,好像是一个月前吧,当时看到 littlecodersh 的仓库上有个新提交的 trip 库,简单看了看,基于 Tornado 和 requests 封装的一个协程库,当时想着,用 python3 的协程库就好了,就没太细看,然后过几天就看到作者在各平台开始宣传这个库了,我对这个库也只是旁观没实际在生产上使用过。但等项目慢慢成熟之后,我肯定是会尝试的。

    再就是关于轮子的看法,关于 python 协程的三方库也有好几个,之所以有这么多,肯定是现存的库或多或少有些不足或者不方便,更重要的是,写个轮子肯定能加深对这方面的理解。你都没看人家的源码,甚至说都没使用过,你就认为是"撸一个自以为是的半吊子的框架出来",说什么"Node.js 环境下就是这种太多了,各种各样的半吊子框架",你有能力吐槽别人写的是半吊子框架,有本事自己写一个出来啊,没本事就别 bb,你不用自然有人用好吧。对于那些吐槽别人库烂的人,我只想说,你不尊重别人的劳动成果也就算了,你可以选择不用,也没人强求你用,问题是,你 tm 都没好好研究过,bb 个毛

    另外对于 Gevent,我想你是真的没有遇到过项目出 bug 是因为 Gevent 替换了底层库造成的,你是不懂这种事情有多坑。
    2225377fjs
        15
    2225377fjs  
       2017-12-15 11:56:25 +08:00
    @xin053
    @NxnXgpuPSfsIT

    不好意思,我们公司内部服务框架不开源,你们还是自娱自乐自己的撸自己的野路子轮子吧。

    如果我说我们之前做服务框架的时候,几人完全阅读掌握了 greenlet 源码及其原理,libev,完全掌握了 gevent 的协程调度模型以及各种实现原理,而且我们内部对 gevent 的协程调度做了很多 hook,这样你觉得我有资格评价 gevent 不?

    正是有这个底气才敢出来说,你以为随便乱说的?
    贴几篇当年学习 greenlet 与 gevent 时候做的记录:

    http://blog.csdn.net/fjslovejhl/article/details/38824963
    http://blog.csdn.net/fjslovejhl/article/details/45956339
    http://blog.csdn.net/fjslovejhl/article/details/39153861

    知道自己水平不行,所以用好现有的轮子就好了,有问题自己就去解决,我有自知之明,不敢随便撸轮子。

    “另外对于 Gevent,我想你是真的没有遇到过项目出 bug 是因为 Gevent 替换了底层库造成的,你是不懂这种事情有多坑。”
    别自己 yy,好不
    2225377fjs
        16
    2225377fjs  
       2017-12-15 12:51:34 +08:00
    @NxnXgpuPSfsIT
    @xin053

    我说他是 翔一样的性能,你不信,随便测一下,下面两段代码自己看看输出吧:

    import trip
    num = 0
    import time
    before = time.time()
    test_time = 200
    all_length = 0

    def main():
    global num
    global all_length

    r = yield trip.get('http://www.ccb.com/')
    num += 1
    all_length += len(r.content)
    if num == test_time:
    after = time.time()
    print after - before
    print "all length :" , all_length

    for _ in xrange(test_time):
    trip.run(main)

    out:
    12.0239999294
    all length : 55200


    -------------------------------------------
    num = 0
    import time
    before = time.time()
    test_time = 200
    all_length = 0



    import gevent.monkey
    gevent.monkey.patch_all()
    import gevent

    import requests
    threads = []


    def _task():
    global all_length
    r = requests.request("GET", "http://www.ccb.com/")
    all_length += len(r.content)

    for _ in xrange(test_time):
    threads.append(gevent.spawn(_task))

    for t in threads:
    t.join()

    print time.time() - before
    print "all length :", all_length

    out:
    0.721999883652
    all length : 55200
    --------------------------------------------------------------

    所以我说是翔一样的性能没有冤枉它吧,或者说只是你这个库的性能是这样的而已。
    2225377fjs
        17
    2225377fjs  
       2017-12-15 12:59:43 +08:00
    @NxnXgpuPSfsIT 或者说我的测试有误,不知道你的库的并发是怎么弄,也不知道你的库怎么弄 join 这种类似的原语,你可以按照你自己的逻辑去测试上面类似的业务,
    并发同步访问 www.ccb.com N 次就行,看看耗时。(暂时没空去研究你的库怎么做这些功能)
    NxnXgpuPSfsIT
        18
    NxnXgpuPSfsIT  
    OP
       2017-12-15 14:44:26 +08:00 via iPhone
    @2225377fjs
    gevent 的侵入导致的 bug 我已经举出了那么多例子,不再多做解释。再次提醒,他会给程序埋下无法发现的隐患。

    很遗憾,我发现你竟然是在完全不了解框架的情况下与我进行的交流。
    所谓的性能差距竟然是来源于你完全不了解 Python 官方主流协程的运行方式,也没有花哪怕三分钟了解一下这个项目或是 tornado 或是 asyncio 的入门。
    你打字的时间远不止三分钟,相较你说的没空,嚣张怕是更适合你。

    关于回答,你依旧无法做出任何实质性的回答。
    很意外,你无法 show me the code,哪怕是一个空余时间的小项目,而论证的唯一一个观点竟是这样的水平。
    然而不出所料的,你依然出言不逊,展示着你的自负。
    抱歉,这样的话,我没有时间和你浪费。

    言尽于此,嚣张和自负背后往往有相适应的能力与才华,我真心希望你有但我没能发现。
    不再回复,祝好!
    2225377fjs
        19
    2225377fjs  
       2017-12-15 16:20:52 +08:00
    @NxnXgpuPSfsIT 哈哈,按你说的我喷你的框架没啥鸟用是嚣张,那你有没有想过自己根本没有深入使用 gevent 就开始说 gevnet 这不行那不行,是不是你也不咋样啊,举出来的例子还全是网上随便搜出来的例子,你能举出你在实际使用功能中遇到了什么天坑的例子么?

    这种随便网上搜一下,然后复制粘贴一下来证明自己的观点,还是那句,是不是有点太快餐了?

    我是没法 show code,因为我知道自己几斤几两,不会轻易去撸轮子,也不喜欢到处贴自己的代码,但是我起码贴出了我自己的记录的博客文章,你可以去看看我够不够格评价 gevent,是不是对 gevent 足够了解呢?

    我确实没有时间去了解你那我看来可能没什么用的库,因为我用 gevent 可以很简单的实现你的各种 sample。

    即便 gevent 有各种问题,好歹人家好用啊,你的 sample 的例子我没看出来哪一个 gevent 实现不了啊。。而且 gevent 实现起来更简单更干净啊。你说我为啥还要花时间去了解你的库呢?

    yes,我最开始就承认自己是普通程序员,所以哪个好用我就用哪个,被 node.js 现在各种半吊子库的环境弄的很恼火,所以很鄙视这种动不动自己就上手撸轮子的行为。

    gevent 是有一些天坑,我们在实际使用中也遇到了坑,但是没有不能解决的问题,你敢保证你的库就没有 bug 了? 按你对的协程的理解和定义,是不是 go 的协程环境也是很不标准呢?毕竟 gevent 的原理机制和 go 非常类似.

    我最开始的问题就是 gevent 能很好的实现你的库展示的各种功能,而且人家能提供的功能更丰富,为啥还要去撸一个轮子,因为这样才能体现自己牛逼?你说 gevent 有问题,那么你请告诉 gevent 实现你演示的各种功能,会遇到什么 bug ?更或者说会遇到什么不能解决的 bug ?

    在实际生产环境下到底哪个库更堪用一些,你既然要上网公开环境的推广你自己的库,那么你起码你要有一定的理由告诉我这一开始就对你的库表示质疑的人吧,这种 gevent 有 a,b,c 等等问题,网上一搜,一罗列的回答,显然这没有啥说服力吧,这就是我一开始的问题。

    而且对你这样回答:“ gevent 加 patch 永远也不知道会出什么问题,怎么和别的协程框架配合,你可以网上搜一下相关内容的吐槽”, 你应该说“你永远不知道会出什么问题”,还上网搜个毛啊,你都不知道出什么问题,搜出来的都是跟你一样的,有何用。
    guyskk0x0
        20
    guyskk0x0  
       2017-12-15 17:02:30 +08:00   ❤️ 1
    @2225377fjs #19 我认为你根本不懂协程,也完全不了解框架

    你说 "trip 翔一样的性能",是因为你不懂协程,你的测试程序根本没有并发。

    楼主帖子里有这样一段,你看出哪里不严谨了吗?

    ```
    r = yield [future1, future2, future3]
    没错,这样三个操作就同时跑了起来 (我知道这不严谨,但看出这不严谨的也不需要我给你夸耀协程的好处了吧)。
    ```

    楼主也举了 gevent 的例子,你说你懂 gevent,麻烦解释一下?

    ```
    关于隐患,先上一个特别简单的代码:
    import multiprocessing, grequests; manager = multiprocessing.Manager()
    他报错了,如果不是这段代码非常简单的只有一个引用,你需要多久要怎么发现是 gevent 的问题?
    另外,这个问题你需要怎么修复?
    ```

    不懂就别装懂,没有理据就别乱喷。
    NxnXgpuPSfsIT
        21
    NxnXgpuPSfsIT  
    OP
       2017-12-15 17:04:07 +08:00
    贴出最基本的协程的使用,只是用过远没有精通,更不会自视甚高的傲视他人却给出可笑的代码和观点:
    ![]( http://7xrip4.com1.z0.glb.clouddn.com/v2ex/gevent-trip.png)

    Result:
    55200
    0.718000173569
    55200
    12.2539999485
    55200
    0.746000051498

    另外,一个最基本的页面,送给需入门的程序员们:docs.python.org/3/library/asyncio-task.html#coroutine
    2225377fjs
        22
    2225377fjs  
       2017-12-15 21:14:12 +08:00
    @guyskk0x0 几年前就已经完全一行一行的理解了 greenlet 的实现原理,然后你说我不了解协程,现在还迫于无赖,不得不转 go 来做一些业务支持,你要是说我不懂协程,我也没有办法了,我当年看 greenlet,gevent 的文章记录都贴在前面,麻烦你去翻翻,看看我到底懂不懂。

    ```
    关于隐患,先上一个特别简单的代码:
    import multiprocessing, grequests; manager = multiprocessing.Manager()
    他报错了,如果不是这段代码非常简单的只有一个引用,你需要多久要怎么发现是 gevent 的问题?
    另外,这个问题你需要怎么修复?
    ```
    你认为我需要解释这种东西来证明我懂?而且我也不用 grequests,也不用 multiprocessing。


    我现在维护的业务框架是一个分布式服务框架,gevent 环境能非常贴合的满足我们的需求,性能也非常的好,最近公司内部引入了 go 做一些尝试,比较尴尬的是,在类似于基于 mongodb 的登录,基于 redis 的排行榜这种比较简单的服务,在 pypy 跑下来,吞吐数据已经和 go 不相上下了,在 gevent 环境下很简单的就把这些库拿进来用就行了,而且有项目组使用的 redis 的 pub-sub 功能,还有用 kafka 的项目组,你觉得现在 Python 环境下还有什么框架能更适合开发这么一个业务环境?(不过即使是 pypy 在带复杂业务逻辑的情况下执行效率比起 go 还是差一些,而且 go 还有多线程支持,可以很简单就能实现一些并行任务的开发,这个 Python 环境下很难做啊,哎,仿佛已经看到了我们的 Python 框架的落幕日了)

    既然要提到 multiprocessing,我不知道你们在协程环境下想用 multiprocessing 做啥,我告诉你一个我们的用例:
    登录验证环境,需要对用户数据进行 基于 rsa 的签名,但是 rsa 太慢,导致即使在 pypy 环境下单进程吞吐也只有几百不到一千,部分原因也是因为 cryptograph 库退化掉了,所以 pypy 和 cpython 没啥差,会严重影响 servcie_gate 的响应,这种场景,想要提高单节点的吞吐只有用并行多线程或者 multiprocessing,不过 multiprocessing 其实也搓的一逼,延迟还大,我们最终的做法是用 cffi 加 boost 库包装了一个多线程扩展库,同时兼容 cpython 和 pypy,业务提交加密签名的任务到库,算完之后,通知 libev 的 loop 唤醒挂起的 greenlet 协程提交结果。这种情况,我们的 servcie_gate 即使全部跑授权签名,也能跑 1W+的吞吐,不过代价是进程 CPU 会跑到百分之好几百。

    说了一个我遇到的业务场景,那么礼尚往来,你告诉我一个你遇到的类似的业务场景你是怎么处理的呢?既然你评价我不懂协程。你要是质疑这个业务环境的真实性,那确实没有办法,我还要吃饭,这些东西没法放出来,公司比较保守,不怎么搞开源,曾经有同事放了一点带公司内部邮箱的代码到 github 都被劝退了,太吓人,而且也深知水平有限,不敢大张旗鼓宣传开源自己的东西。

    不过可以给你贴一部分代码: https://github.com/2225377fjs/weblogviewer 这个是很多年前做的一个业务系统,基于 web 页面实时 tail 服务器集群各个机器的 log 文件,server 部分是一个很稚嫩的服务框架原型,还在读书的时候模仿支付宝的那套框架写的东西,基本已经废弃了,不过一些 IO 层的东西现在一直沿用。


    @NxnXgpuPSfsIT 你贴出来不就完了么,那么说明在 200 个这种情况下还是可以的嘛,我已经说过没有兴趣去了解你的框架的 API,以及他是怎么并发的,我能做的就是按照最通俗的 API 和原语去完成功能,另外不要秀代码了,没啥意义,这些语法糖根本不屑于去用,代码是写给别人看的,用最通俗的代码写给所有可能会跟你配合做业务,写代码的人,我们公司有大量的业务逻辑是用 python 写的,这种时候你就能体会那些看起来很简练的语法糖是多么让人讨厌了,宁愿让代码稍微长一些,也要让代码一眼就能看懂。

    另外你很喜欢用 tornado 嘛,同样做 hello world 的 http 服务器,把 wsgi server 换成 gevent 的实现,吞吐还能往上涨额,你可以试试。

    你最终还是没有回答我的问题啊,既然 gevent 很好很方便实现的功能,你自己再撸一个功能还远不如别人的轮子请问意义何在?仅仅是秀?

    不要随便上网搜搜,然后贴一下,就说你看,gevent 这么多坑,这么多人说他挫,我看到是用 gevent 非常方便的能实现你框架展示的各种功能啊,至于说坑,你举例子说说你展示的这些功能在 gevent 的环境下实现能有什么坑啊?

    你是公开环境推广自己库的人,也要经得起质疑吧?人家说没啥鸟用,你说别人自视甚高,这种环境下好像推广者的姿态更高一些吧。

    不要纠结各种协程定义概念了,没有标准的概念,各种各样不同的实现,我所能看到的 go 实现的协程模型现在发展势头很好,大家接受度都很高,这种概念与 gevent 的模式非常相似。

    我也不说什么半吊子框架了,收回这句话,是不太尊重,理性讨论。
    2225377fjs
        23
    2225377fjs  
       2017-12-15 22:16:25 +08:00
    @guyskk0x0
    ```
    关于隐患,先上一个特别简单的代码:
    import multiprocessing, grequests; manager = multiprocessing.Manager()
    他报错了,如果不是这段代码非常简单的只有一个引用,你需要多久要怎么发现是 gevent 的问题?
    另外,这个问题你需要怎么修复?
    ```

    只有你这种类似的的渣渣才会说上面的东西简单,我告诉你,我理解的上面的一套东西,以及没有贴出来的可能的业务上下文一点也不简单。

    说我不懂装懂,我看你才是不懂装懂吧,渣渣。

    你眼睛瞎啦,不知道把上面的帖子看完?


    @NxnXgpuPSfsIT
    另外,我是不是已经入门的程序员你还没有资格来评价,我从来不敢说自己精通了什么东西,我能做的就是把好用的东西在自己水平范围内把它用好。不敢随意撸轮子,更不敢泛泛的评价各种有一定知名度的轮子。

    我一开始的回复就是因为在我看来你的框架没有任何存在意义,恕我直言,真的是没有任何意义。与其重复的撸这些轮子,不如好好去了解你觉得不行的 gevent,看看 gevent 实现你这轮子的功能是不是很简单,到底会有什么坑。而不是在这种公开环境下泛泛而谈的说 gevent 这不行,那也坑啥的,然后随便上网搜一下找一堆东西 copy 一下来证明你的观点,你不具备这个资格这么来评价 gevent,你一开始对 gevent 的评价给我的第一感觉就是半罐子水,所以需要怼一下才行。

    看得出你很用心的维护你的框架,但是自己的框架不是通过随便泛泛的怼怼别的框架就能牛逼了。

    自己前后都矛盾,一会普通程序员,一会又大局,细节,还撤出各种东西来背书,有毛用啊,还给协程下定义,非得要基于 yield yield from 这些生成器实现的功能都不齐全的环境才叫协程,那 greenlet,go 的协程,甚至包括 boost 库里面实现的协程他们都时啥东西啊。

    Python 生成器的效率问题,我不想去证明了,gevent 的有一个协程 benchmark,你有精力的话可以去试试,不过好像生成器的要实现这种调度的 benchmark 逻辑都很麻烦。(忘记了是 greenlet 还是 gevent 的了,反正肯定有一个会有 10W 并发协程的调度 benchmark 代码)

    不过你要是有精力实现出来,而且实测生成器的调度效率也不差的话,非常希望你能告诉我结果。
    NxnXgpuPSfsIT
        24
    NxnXgpuPSfsIT  
    OP
       2017-12-15 23:07:03 +08:00
    @2225377fjs
    很棒!尊重是交流的基础。


    所以我们达成共识了,

    由于其独特的切入点,gevent 在根源上存在问题,这是劣势。
    * patch 会给项目带来毁灭性的隐患,且无法预料
    * 不区分协程函数和普通函数(不使用 yield )是一个很糟糕的习惯
    * 侵入式的兼容只能产生一个不可知的过渡用品,而不是一个有保障的产品

    而你说的优势,你举出的观点最好的性能是源于你当时对于主流协程的不了解。
    至于解释,你说了你不用,你不愿意解释。但你也认识到了那些问题都是普通用户在日常使用可能中会碰到的。

    我们于是能清楚的看到大牛所说的希望可以有项目出现的空白,
    你想要使用协程网络库,现在有了不存在根本层面问题的选择。


    所以最终回到我的观点,gevent 是一个没有办法的将就。
    Requests 的作者说他想要一个这样库却没有时间,我们有。
    不敢说有什么水平,起码达到了高效、可用,
    起码可以完美的完成就像文中介绍的操作。
    我们才分享了项目的介绍,给那些也曾经意识到这块空白的程序员。
    我们可以一起使用,提意见,志同道合可以一起开发。

    而对于那些幸运的能一直处在 gevent 安全区中的用户或者项目已经基于 gevent 的用户,
    这个项目只是给你们多一个选择,即使你换用官方的协程,依旧可以使用到协程 Requests。
    lolizeppelin
        25
    lolizeppelin  
       2017-12-16 00:51:57 +08:00 via Android
    总体上来说 openstack 都在用 eventlet 所以稳定性上还是有保证的

    猴子补丁确实坑 假的 socket timout 问题找了半天
    这还是我比较熟悉 eventlet 的情况


    如果有选择的话 我宁愿不使用猴子补丁

    但是有选择么?你能写一个 request 的携程库 其他的咋办
    redis mysql mq 你全写一份?这做法你可以换 go 了性能更好

    所以 要携程老老实实用猴子补丁咯
    guyskk0x0
        26
    guyskk0x0  
       2017-12-16 12:47:06 +08:00 via Android
    @NxnXgpuPSfsIT
    1. 你说你懂协程,却写了个错误的测试代码,来喷 “ trip 翔一样的性能”,你在 #16 楼贴的代码足以证明你不懂协程。

    2. 你说你对 gevent 了如指掌,遇到的问题都能解决,那为何不能回答 #20 楼的问题?你那几篇博客,顶多说明你看过源码。你解决过官方仓库的 issue 吗?你给上游贡献过代码吗?你能质疑这个项目,我就不能质疑你吗?

    3. gevent 暗坑很多,这是事实。Python 核心开发者,包括 Python 之父 Guido 和 Requests 作者都表态了 gevent 不行,asyncio 以及 async/await 语法都已经加到 Python 中了,协程是解决问题的方向,是未来。你连协程的基本原理都不懂,没资格说这个项目没鸟用。

    4. gevent 是当下的 “将就” 解决方案,大部分项目确实是用 的 gevent,因为协程并不成熟,生态并不完善,我们没得选
    。但是 gevent 问题很多,这是众所周知的事,我们尝试新的解决方案很有意义啊,你看不懂不代表别人也看不懂。
    lolizeppelin
        27
    lolizeppelin  
       2017-12-16 14:31:03 +08:00
    https://wiki.openstack.org/wiki/Oslo/blueprints/asyncio

    看上面, 跟着 openstack 走就好, 起码目前 eventlet 的方式是可行的

    gevent 是将就,但是目前确实不是能很好替代的,等 openstack 用上 asyncio 基本就有一系列可靠方案了
    sunwei0325
        28
    sunwei0325  
       2017-12-17 00:27:50 +08:00
    gevent 迟早和今天的 python2 一样被打上末日标记
    mmz0707
        29
    mmz0707  
       2017-12-20 11:47:50 +08:00
    有好的代理推介吗
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3237 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 14:11 · PVG 22:11 · LAX 06:11 · JFK 09:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.