<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://www.bigtalk.xyz/blog</id>
    <title>Mr.Yu's Blog Blog</title>
    <updated>2021-06-01T20:10:23.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://www.bigtalk.xyz/blog"/>
    <subtitle>Mr.Yu's Blog Blog</subtitle>
    <icon>https://www.bigtalk.xyz/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[互联网见闻 2000 - 2020]]></title>
        <id>/2021/06/01/</id>
        <link href="https://www.bigtalk.xyz/blog/2021/06/01/"/>
        <updated>2021-06-01T20:10:23.000Z</updated>
        <summary type="html"><![CDATA[还记得在2000年初，刚接触到电脑的时候，一直感叹网络的神奇：你可以与相隔千万里的人一起聊天，听音乐，玩游戏！受限于当时的硬件水平和网络环境，各种应用还比较少，比较大的乐趣就是网络聊天，游戏和浏览一些视频，音乐门户网站等。那会还是资源匮乏的年代，网络上的新闻，媒体资源相对质量还是比较高的；同时也是盗版泛滥发展的时期。随着互联网行业的发展，越来越多的企业和个人进入到行业里面，应用和资讯开始丰富起来。有句话是说：有人的地方就就有江湖，有江湖的地方就有纷争，互联网也自然难以幸免。]]></summary>
        <content type="html"><![CDATA[<p>还记得在2000年初，刚接触到电脑的时候，一直感叹网络的神奇：你可以与相隔千万里的人一起聊天，听音乐，玩游戏！受限于当时的硬件水平和网络环境，各种应用还比较少，比较大的乐趣就是网络聊天，游戏和浏览一些视频，音乐门户网站等。那会还是资源匮乏的年代，网络上的新闻，媒体资源相对质量还是比较高的；同时也是盗版泛滥发展的时期。随着互联网行业的发展，越来越多的企业和个人进入到行业里面，应用和资讯开始丰富起来。有句话是说：有人的地方就就有江湖，有江湖的地方就有纷争，互联网也自然难以幸免。</p><p>2010年以前，可以称为<code>桌面互联网</code>时代，主要的上网媒介还是PC和笔记本电脑。互联网企业主要战场也是在客户端应用和浏览器端，各种应用像社交聊天，搜索，电商，安全，浏览器，游戏，媒体，办公软件等层出不穷，也在此阶段涌现出对应的互联网企业巨头。在利益的驱动下，巨头之间互相争夺用户，形成各自的护城河，自然也上演了不少闹剧。在巨头进入垄断的过程中，信息也开始圈在各自的护城河中，此时为信息蔽塞的开始。</p><p>自2010年以后，伴随智能手机的普及，正式进入<code>移动互联网</code>时代，互联网企业开始逐鹿移动端市场。有了PC端的成熟经验后，各企业在此过程中的竞争异常激烈。原先PC时代的互联网巨头，有的因转型慢进错跑道而衰落，有的抢占先机进一步壮大；同时也诞生一些新贵，逐步成为新的巨头。无一例外，所有的成功者都基本对一个行业或是某个应用场景实现垄断。同时盈利模式也出奇的一致：</p><ul><li>前期免费甚至补贴用户，培养用户习惯</li><li>中期完善业务功能，功能不断膨胀，提升用户存留</li><li>后期垄断形成，开始收割盈利</li></ul><p>同时，由于移动端的便携和碎片化的特性，用户在各应用上花费的时间是远远超过PC端的，甚至部分PC主要的办公领域也被移动端蚕食。结果导致互联网企业将资源不断向移动端APP倾斜，以致于很多原本在web端能轻松访问的功能，都受到各种限制。像下图的情况相信很多人都有遇到。
<img loading="lazy" alt="img" src="/assets/images/2021-06-01-67ccd9167460828dc7d5173b8815905b.jpeg" width="1080" height="2160" class="img_qnnJ">
若说桌面互联网时代，是信息从开放走向蔽塞的开始，则移动互联网时代，则是进一步加深此现象。纵观如今的各类应用，同样的开屏广告，同样的推广推荐，同样的VIP，SVIP，同样的各种超级权限获取，同样的收费和盈利模式。作为一个因技术和创新所发展出来的行业，是不是应该问个为什么？</p><p>互联网出现的初衷，是为了使信息能够得到更好的传播与分享，本应更好的服务于大众。然而现在一个个的应用APP，将用户以各种形式圈禁在自己的领域范围内，在各种推荐算法之下，推送各种自诩高明的资讯与推广，以获取用户的关注与存留，这究竟是用户的真实需求还是资本的贪婪。对于那些从一开始就习惯现在各种APP的互联网用户，或许会觉得这一切是那么的自然。然而，作为该领域从业者的一员，我觉得事情本不该如此。我们塑造了生活，生活也改变了我们；我们塑造了互联网，互联网也会影响我们。资本，垄断，挣钱无可厚非，但绝不该一条路走到黑。信息的价值，在于流动，互联网应该是信息的河流，绝不该成为信息的围城。未来现行的这些模式终归会被打破，期待新型的互联网模式实现。</p>]]></content>
        <category label="互联网" term="互联网"/>
        <category label="信息" term="信息"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[celery笔记]]></title>
        <id>/2016/10/02/</id>
        <link href="https://www.bigtalk.xyz/blog/2016/10/02/"/>
        <updated>2016-10-02T15:10:28.000Z</updated>
        <summary type="html"><![CDATA[1.何为消息队列?]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorWithStickyNavbar_SIhB" id="1何为消息队列">1.何为消息队列?<a class="hash-link" href="#1何为消息队列" title="标题的直接链接">​</a></h2><p>在不同系统(或物理设备)之间，应用软件之间，程序进程之间，常常会有各种互相的信息传递；为保证消息传递的可靠性，对所传消息引入一个保存的容器：一方面用来接收发送者产生的信息，一方面在接收者正常的情况下完成消息的派送，并在无法接收消息时对信息进行存储，然后在适当的时机完成信息的派送。一般称该容器为消息队列。</p><h2 class="anchor anchorWithStickyNavbar_SIhB" id="2消息队列解决什么问题">2.消息队列解决什么问题?<a class="hash-link" href="#2消息队列解决什么问题" title="标题的直接链接">​</a></h2><p>对于部分需要较长时间处理的任务类型，采用传统的同步处理方式会带来较长时间的性能损耗或是不良的用户体验，如上传文件处理，图像压缩，发送邮件等，此类实时性要求不是特别高的任务类型，采用异步消息队列的模式可以提升处理效率。</p><p><img loading="lazy" alt="celery" src="/assets/images/celery-1-e82dfdc482e74878886ea18407bf05de.png" width="1018" height="234" class="img_qnnJ">
<img loading="lazy" alt="celery" src="/assets/images/celery-2-d0614fb49972499e8211026a5425c66b.png" width="1196" height="458" class="img_qnnJ"></p><h2 class="anchor anchorWithStickyNavbar_SIhB" id="3celery简介">3.Celery简介<a class="hash-link" href="#3celery简介" title="标题的直接链接">​</a></h2><p>一个基于消息通信的任务调度分发处理框架，由python语言实现。具体工作过程可以归纳如下：
1.预先定义好一系列需要执行的操作(对应python中的函数)，称之为consumers，将其注册到Celery的task中。
2.启动Celery服务后，相关的workers会在各自单独的进程中持续监听消息队列，一旦接收到调用消息，
worker就会根据相关配置找到consumer执行,即执行步骤1中定义的函数。
3.任务的发起者(或称之为producer)，在调用相关worker之前，需先导入Celery配置环境，然后向指定的worker发送相关消息。
4.Celery中的消息接收和分发采用第三方消息中间件，如： RabbitMQ、Redis，MongoDB等，称之为broker。同时，这些消息中间件也可用来存放worker的执行结果(称为backend)。</p><p><img loading="lazy" alt="celery" src="/assets/images/celery-3-23db331ea1740302dfc0cb4db499516b.png" width="928" height="516" class="img_qnnJ"></p><h2 class="anchor anchorWithStickyNavbar_SIhB" id="4celery基本使用">4.Celery基本使用<a class="hash-link" href="#4celery基本使用" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_SIhB" id="1-安装">1. 安装：<a class="hash-link" href="#1-安装" title="标题的直接链接">​</a></h3><p>celery的Python包：pip install celery
celery依赖的消息中间件，安装方式参考：<a href="http://docs.celeryproject.org/en/latest/getting-started/brokers/rabbitmq.html#broker-rabbitmq" target="_blank" rel="noopener noreferrer">RabbitMQ</a>，<a href="http://docs.celeryproject.org/en/latest/getting-started/brokers/redis.html#broker-redis" target="_blank" rel="noopener noreferrer">Redis</a>，<a href="https://docs.mongodb.com/manual/installation/" target="_blank" rel="noopener noreferrer">MongoDB</a></p><h3 class="anchor anchorWithStickyNavbar_SIhB" id="2-基本用法以mongodb作为消息中间件">2. 基本用法（以MongoDB作为消息中间件）：<a class="hash-link" href="#2-基本用法以mongodb作为消息中间件" title="标题的直接链接">​</a></h3><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">#建立main.py，输入以下内容：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from celery import Celery</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app = Celery(name=‘task', broker='mongodb://localhost:27017/workers_tasks') #name为app实例的名称，非必须，broker为消息中间件的连接地址</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#本示例采用本地mongoldb,在启动服务前需确保mongodb已经启动并可正常连接。若采用远程服务器，则配置成如下格式：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#'mongodb://userid:password@hostname:port/database_name'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#broker属于app实例配置部分，可通过app.config_from_object(object),app.config_from_envvar(env_name),app.conf.update()等方式配置</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#定义一个函数,注册到task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@app.task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">def sayhi():</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     return 'hi'</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_SIhB" id="注详细配置参见官方网站"><strong>注：详细配置参见<a href="http://docs.celeryproject.org/en/latest/configuration.html#configuration" target="_blank" rel="noopener noreferrer">官方网站</a></strong><a class="hash-link" href="#注详细配置参见官方网站" title="标题的直接链接">​</a></h4><h3 class="anchor anchorWithStickyNavbar_SIhB" id="3启动方式">3.启动方式：<a class="hash-link" href="#3启动方式" title="标题的直接链接">​</a></h3><p>终端模式进入到main.py所在目录，执行</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">celery -A main worker --loglevel=debug</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>一般出现以下画面表示启动成功。</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">[tasks]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  . celery.backend_cleanup</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  . celery.chain</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  . celery.chord</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  . celery.chord_unlock</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  . celery.chunks</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  . celery.group</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  . celery.map</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  . celery.starmap</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  . main.sayhi</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,215: DEBUG/MainProcess] | Worker: Starting Pool</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,233: DEBUG/MainProcess] ^-- substep ok</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,234: DEBUG/MainProcess] | Worker: Starting Consumer</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,234: DEBUG/MainProcess] | Consumer: Starting Connection</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,248: INFO/MainProcess] Connected to mongodb://localhost:27017/workers_tasks</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,249: DEBUG/MainProcess] ^-- substep ok</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,249: DEBUG/MainProcess] | Consumer: Starting Events</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,259: DEBUG/MainProcess] ^-- substep ok</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,259: DEBUG/MainProcess] | Consumer: Starting Heart</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,262: DEBUG/MainProcess] ^-- substep ok</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,262: DEBUG/MainProcess] | Consumer: Starting Tasks</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,266: DEBUG/MainProcess] ^-- substep ok</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,266: DEBUG/MainProcess] | Consumer: Starting Control</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,273: DEBUG/MainProcess] ^-- substep ok</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,273: DEBUG/MainProcess] | Consumer: Starting event loop</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,273: WARNING/MainProcess] celery@Work ready.</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 00:36:39,273: DEBUG/MainProcess] basic.qos: prefetch_count-&gt;32</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>此时启动了一个worker实例，<!-- -->[tasks]<!-- -->中显示的为监听该worker的consumers，包括我们在main.py中定义的sayhi。</p><h4 class="anchor anchorWithStickyNavbar_SIhB" id="相关说明-a表示启动应用实例main是包含celery实例的文件或是包在本示例中为mainpyworker为启动实例的运行模式该模式下所有定义于main中的task将全部进入监听模式">相关说明：-A表示启动应用实例，main是包含celery实例的文件或是包(在本示例中为main.py)，worker为启动实例的运行模式，该模式下所有定义于main中的task将全部进入监听模式。<a class="hash-link" href="#相关说明-a表示启动应用实例main是包含celery实例的文件或是包在本示例中为mainpyworker为启动实例的运行模式该模式下所有定义于main中的task将全部进入监听模式" title="标题的直接链接">​</a></h4><h4 class="anchor anchorWithStickyNavbar_SIhB" id="logleveldebug指定log级别为debug模式此时会有较多的信息列印至终端便于分析问题">—loglevel=debug:指定log级别为debug模式，此时会有较多的信息列印至终端，便于分析问题。<a class="hash-link" href="#logleveldebug指定log级别为debug模式此时会有较多的信息列印至终端便于分析问题" title="标题的直接链接">​</a></h4><h4 class="anchor anchorWithStickyNavbar_SIhB" id="在生产环境中常需要以守护进程的方式在后台运行可使用supervisor">在生产环境中，常需要以守护进程的方式在后台运行，可使用supervisor<a class="hash-link" href="#在生产环境中常需要以守护进程的方式在后台运行可使用supervisor" title="标题的直接链接">​</a></h4><h4 class="anchor anchorWithStickyNavbar_SIhB" id="注通过celery-help可以查看更多的启动命令提示"><strong>注：通过celery help可以查看更多的启动命令提示。</strong><a class="hash-link" href="#注通过celery-help可以查看更多的启动命令提示" title="标题的直接链接">​</a></h4><h3 class="anchor anchorWithStickyNavbar_SIhB" id="4调用方式">4.调用方式：<a class="hash-link" href="#4调用方式" title="标题的直接链接">​</a></h3><p>另起一终端环境，进入main.py所在目录，启动python解释环境，执行以下代码：</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; import main</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; main.sayhi.delay()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;AsyncResult: 7dbd1acb-cd88-402f-bc6c-8c8b386ff544&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#此时在启动celery服务的终端中，可以看到如下类似消息，表示调用任务成功</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 01:21:23,650: INFO/MainProcess] Received task: main.sayhi[7dbd1acb-cd88-402f-bc6c-8c8b386ff544]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 01:21:23,651: DEBUG/MainProcess] TaskPool: Apply &lt;function _fast_trace_task at 0x110d802a8&gt; (args:('main.sayhi', '7dbd1acb-cd88-402f-bc6c-8c8b386ff544', [], {}, {'utc': True, u'is_eager': False, 'chord': None, u'group': None, 'args': [], 'retries': 0, u'delivery_info': {u'priority': 0, u'redelivered': None, u'routing_key': u'celery', u'exchange': u'celery'}, 'expires': None, u'hostname': 'celery@Work', 'task': 'main.sayhi', 'callbacks': None, u'correlation_id': u'7dbd1acb-cd88-402f-bc6c-8c8b386ff544', 'errbacks': None, 'timelimit': (None, None), 'taskset': None, 'kwargs': {}, 'eta': None, u'reply_to': u'3057b462-bedc-3c51-9ea9-83094c8771e7', 'id': '7dbd1acb-cd88-402f-bc6c-8c8b386ff544', u'headers': {}}) kwargs:{})</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 01:21:23,653: DEBUG/MainProcess] Task accepted: main.sayhi[7dbd1acb-cd88-402f-bc6c-8c8b386ff544] pid:34352</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2016-09-24 01:21:23,653: INFO/MainProcess] Task main.sayhi[7dbd1acb-cd88-402f-bc6c-8c8b386ff544] succeeded in 0.000943564984482s: hi</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_SIhB" id="注调用worker时要采用delay方法触发异步调用delay为apply_async方法的简易版本可满足常规需求"><strong>注：调用worker时，要采用delay方法触发异步调用。delay为apply_async方法的简易版本，可满足常规需求。</strong><a class="hash-link" href="#注调用worker时要采用delay方法触发异步调用delay为apply_async方法的简易版本可满足常规需求" title="标题的直接链接">​</a></h4><h4 class="anchor anchorWithStickyNavbar_SIhB" id="如需特殊调用方式可参考官方文档关于apply_async的相关说明"><strong>如需特殊调用方式，可参考官方文档关于<a href="http://docs.celeryproject.org/en/latest/userguide/calling.html" target="_blank" rel="noopener noreferrer">apply_async</a>的相关说明。</strong><a class="hash-link" href="#如需特殊调用方式可参考官方文档关于apply_async的相关说明" title="标题的直接链接">​</a></h4><p>在目前情况下，无法获取到函数的返回结果，若需对结果进行存档或是追踪，需要设置backend属性，在main.py中添加以下代码</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">#该配置信息指定worker执行完成后，存储结果状态信息的位置。</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app.conf.update(CELERY_RESULT_BACKEND='mongodb://localhost:27017/',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                CELERY_MONGODB_BACKEND_SETTINGS={'database': 'celery_dev_db', 'taskmeta_collection': 'workers_tasks_result'})</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>重新启动Celery服务，在调用程序终端中执行以下语句</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res = main.sayhi.delay()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res.ready()               #worker执行完毕，返回True,否则为False</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">True</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res.get()                 #返回worker函数的返回值   </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">'hi'</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_SIhB" id="注一般情况下不会采用上述的方式来确认worker的返回结果因为此方式将异步调用变成了同步调用失去使用celery的意义"><strong>注：一般情况下不会采用上述的方式来确认worker的返回结果，因为此方式将异步调用变成了同步调用，失去使用celery的意义。</strong><a class="hash-link" href="#注一般情况下不会采用上述的方式来确认worker的返回结果因为此方式将异步调用变成了同步调用失去使用celery的意义" title="标题的直接链接">​</a></h4><h4 class="anchor anchorWithStickyNavbar_SIhB" id="在需要追踪结果的情况下可以采用producer轮询backend或是由worker发送消息至producer"><strong>在需要追踪结果的情况下，可以采用producer轮询backend或是由worker发送消息至producer。</strong><a class="hash-link" href="#在需要追踪结果的情况下可以采用producer轮询backend或是由worker发送消息至producer" title="标题的直接链接">​</a></h4><h2 class="anchor anchorWithStickyNavbar_SIhB" id="celery进阶">Celery进阶<a class="hash-link" href="#celery进阶" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_SIhB" id="a定时任务">a.定时任务：<a class="hash-link" href="#a定时任务" title="标题的直接链接">​</a></h3><p>celery可以支持定时任务，预先配置好相关参数，启动服务后可以自动执行。</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain"># 示例，在main.py中增加以下内容</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from datetime import timedelta</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">add_schedule = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    'add-every-5-seconds': {'task': 'main.add', 'schedule': timedelta(seconds=5), 'args': (1, 2)}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app.conf.update(CELERYBEAT_SCHEDULE=add_schedule)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@app.task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">def add(x,y):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     return x+y</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 以上内容创建一个定时5s自动执行的任务，调用add函数，参数为(1,2)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 启动方式,通过beat参数执行：celery -A main beat —-loglevel=debug</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 也可以与worker同时启动：celery -A main worker -B —loglevel=debug</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 在需要对定时任务做更精确的控制的情况下，可以采用celery.schedules中的crontab来实现</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 例如，将上述间隔5s的add任务,改变为每周一的早上7：30执行，可以采用如下配置方式：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># from celery.schedules import crontab</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># add_schedule = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#     "add-every-monday-morning": {“task”:”main.add”,”schedule":crontab(hour=7, minute=30, day_of_week=1), "args":(1, 2)}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># app.conf.update(CELERYBEAT_SCHEDULE=add_schedule)</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_SIhB" id="注设置定时任务时需注意celery_timezone相关设置详细设置参考"><strong>注：设置定时任务时需注意CELERY_TIMEZONE相关设置,详细<a href="http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html" target="_blank" rel="noopener noreferrer">设置参考</a></strong><a class="hash-link" href="#注设置定时任务时需注意celery_timezone相关设置详细设置参考" title="标题的直接链接">​</a></h4><h3 class="anchor anchorWithStickyNavbar_SIhB" id="b工作流">b.工作流<a class="hash-link" href="#b工作流" title="标题的直接链接">​</a></h3><p>celery可以对task进行调用顺序的设置，形成一个任务执行流，对于要加入任务流的函数，需将其进行’签名’操作（或称为建立子任务)。
下面以main.py中的add函数来作为示例：</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; from celery import signature</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># new_add作为一个add签名，参数为(2，2),调用new_add 10s后执行add(2,2）</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; new_add=signature('main.add', args=(2, 2), countdown=10)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; new_add</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">main.add(2, 2)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; new_add.delay()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;AsyncResult: e5afb013-f907-4111-a9df-d3917237f2fb&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#以上代码可以采用subtask实现相同效果：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; new_add=add.subtask((2,2),countdown=10)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; new_add</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">main.add(2, 2)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#还有一种简写方式：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; new_add = add.s(2,2)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; new_add</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">main.add(2, 2)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#签名支持部分参数传入</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; new_add1 = add.s(2)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res = new_add1.delay(3)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res.get()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">5</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt;</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>上述签名的应用方式看起来似乎用处不大，但签名可以通过delay和apply_async传入参数的方式调用，这样可以将任务组装成工作流。</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain"># 通过link将签名任务链接到当前执行的任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res=add.apply_async((5,2),link=add.s(9))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res.get()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">7</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res.children[0].get()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">16</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; list(res.collect())</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[(&lt;AsyncResult: 371f21c7-d546-45ef-b5b7-dea124114754&gt;, 7), (&lt;AsyncResult: 24938b03-ad1a-41dd-91de-61bbc5cce851&gt;, 16)]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 通过chain组合多个子任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; from celery import chain</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res = chain(add.s(1, 2), add.s(3), add.s(4)).delay()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res.get()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">10</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#通过group组合多个子任务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; from celery import group</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res = group(add.s(1, 1), add.s(2, 2), add.s(3, 3), add.s(4, 4)).delay()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; res.get()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[2, 4, 6, 8]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt;</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>关于工作流的应用，celery还有内置chord，map，starmap，chunks等内置方法，详细使用方式参考<a href="http://docs.jinkan.org/docs/celery/userguide/canvas.html#guide-canvas" target="_blank" rel="noopener noreferrer">官方文档</a></p><h3 class="anchor anchorWithStickyNavbar_SIhB" id="c消息分发与任务调度的实现机制">c.消息分发与任务调度的实现机制：<a class="hash-link" href="#c消息分发与任务调度的实现机制" title="标题的直接链接">​</a></h3><p>producer发出调用请求(message包含所调用任务的相关信息)—&gt;celery服务启动时，会产生一个或多个交换机(exchanges)，对应的交换机 接收请求message—&gt;交换机根据message内容，将message分发到一个或多个符合条件的队列(queue)—&gt;每个队列上都有一个或多个worker在监听，在监听到符合条件的message到达后，worker负责进行任务处理，任务处理完被确认后，队列中的message将被删除。</p><p><img loading="lazy" alt="celery" src="/assets/images/celery-4-cc112081fd9030e4e2964a39eeabf20b.png" width="1746" height="1196" class="img_qnnJ"></p><p>celery支持所有AMQP(Advanced Message Queuing Protocol:高级消息队列协议）路由机制，可以通过配置的方式，执行相关的
消息路由路径。</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain"># 在main.py中增加以下代码</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from kombu import Exchange, Queue</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#增加两个task,test_queue_1与test_queue_2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@app.task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">def test_queue_1():</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    return 'queue1'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@app.task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">def test_queue_2():</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    return 'queue2'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># queue_1与queue_2为消息队列名称</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># Exchange:为交换机实例，具有不同的类型。详细参考</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># routing_key:用来告知exchange将task message传送至相对应的queue</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">queue = (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Queue('queue_1', Exchange('Exchange1', type='direct'), routing_key='queue_1_key'),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Queue('queue_2', Exchange('Exchange2', type='direct'), routing_key='queue_2_key')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">route = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    'main.test_queue_1': {'queue': 'queue_1', 'routing_key': 'queue_1_key'},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    'main.test_queue_2': {'queue': 'queue_2', 'routing_key': 'queue_2_key'}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app.conf.update(CELERY_QUEUES=queue, CELERY_ROUTES=route)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">#########</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 为直观的观察效果，开启终端1运行以下命令，监听queue_1队列：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">celery -A main worker -Q queue_1 --loglevel=debug</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 开启终端2运行以下命令，监听queue_2队列：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">celery -A main worker -Q queue_2 --loglevel=debug</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 此时调用main.py中的test_queue_1和test_queue_2，会发现task被分发到各个对应的celery worker服务。</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 对于没有被队列接收的sayhi函数，通过sayhi.apply_async(queue='queue_1’)可以将任务分发到queue_1</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>celery的广播模式(该模式仅仅在broker采用RabbitMQ，Redis才可以使用</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain"># 在main.py中，将broker='mongodb://localhost:27017/workers_tasks' 改为broker='amqp://'，同时启动本地rabbitmq服务</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 添加以下代码，</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">from kombu.common import Broadcast</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@app.task</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">def broadcast():</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    return 'broadcast'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">queue_bor = (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Broadcast('broadcast_tasks’), #此处设置消息队列broadcast为广播模式，及该队列上的消息会发送至所有监听它的worker</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Queue('broadcast_tasks'),</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">queue_route = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    'main.broadcast': {'queue': 'broadcast_tasks'},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">app.conf.update(CELERY_QUEUES=queue_bor, CELERY_ROUTES=queue_route)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"># 按以上配置后，同时在两个终端启动celery服务，通过broadcast.delay()调用task时，会发现两个celery实例均有执行broadcast函数。</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>基于上述内容，可以应付大部分的异步消息处理的情况。若需进一步深入，请参阅<a href="http://docs.jinkan.org/docs/celery/" target="_blank" rel="noopener noreferrer">官方文档</a></p>]]></content>
        <category label="celery" term="celery"/>
        <category label="消息队列" term="消息队列"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[react组件生命周期]]></title>
        <id>/2016/07/02/</id>
        <link href="https://www.bigtalk.xyz/blog/2016/07/02/"/>
        <updated>2016-07-02T16:06:11.000Z</updated>
        <summary type="html"><![CDATA[在react框架中，组件是构成一切应用的基石，整个页面的渲染与操作，均是在组件层面来完成。按照其官网的说法，]]></summary>
        <content type="html"><![CDATA[<p>在react框架中，组件是构成一切应用的基石，整个页面的渲染与操作，均是在组件层面来完成。按照其官网的说法，
每一个组件可以当做为一个virtual DOM节点，所有的数据传递及状态改变均是在此层面完成，然后通过render方法
渲染至真实的DOM。而基于virtual DOM的diff算法，也被认为是该框架具备高效操作DOM的原因。(关于react框架与
原生js操作DOM效率问题，网上有较多的比较，个人暂时没有进行过深入的研究；不过在实际应用层面来说，还是得
结合现实的业务情景。毕竟再好的锤子，都不适合用来砍树)作为在应用react框架过程中，很容易因为组件的操作
不当导致整个的页面异常，甚至根本不显示，因此了解组件相关的生命周期就显得比较重要。</p><p>针对组件的渲染过程，通过代码可以比较直观的观察：
在mian.js中，编辑如下代码：</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">import React from 'react';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">import ReactDOM from 'react-dom';</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">class HelloWorld extends React.Component {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    constructor(props) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        super(props);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        this.state = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            default_value: 'default'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        };</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('constructor:default_value is default');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    componentWillMount() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        this.setState({</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            default_value: 'WillMount_value'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('componentWillMount:default_value is WillMount_value');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    componentDidMount() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        this.setState({</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            default_value: 'DidMount_value'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('componentDidMount:default_value is DidMount_value');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    componentWillReceiveProps(nextProps) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('componentWillReceiveProps:')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    shouldComponentUpdate(nextProps, nextState) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('shouldComponentUpdate:');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        return true</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    componentWillUpdate(nextProps, nextState) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('componentWillUpdate:')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    componentDidUpdate(prevProps, prevState) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('componentDidUpdate:')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    componentWillUnmount() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('componentWillUnmount:')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    render() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('render begin');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        return (&lt;div&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            &lt;h1&gt;Test component life time&lt;/h1&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            &lt;p&gt;{this.state.default_value}&lt;/p&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        &lt;/div&gt;)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ReactDOM.render(&lt;HelloWorld/&gt;, document.getElementById('content'));</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>通过运行程序，观察到控制台输出内容如下：</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">constructor:default_value is default</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">componentWillMount:default_value is WillMount_value</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">render begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">componentDidMount:default_value is DidMount_value</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">shouldComponentUpdate:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">componentWillUpdate:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">render begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">componentDidUpdate:</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>以上反应出整个react组件的渲染过程：
首先进入construct，初始化相关参数，接着进入到componentWillMount,对即将渲染的组件进行进一步
的设置或是状态的更改，此处可以改变一些默认行为(例如进一步设置state，不会引起重新渲染)，
在此部分之后，通过render方法组件完成挂载，即渲染到真实DOM结构。若组件定义了componentDidMount
方法，则会在渲染完成后立即调用，一般会在此处进行setState设置。在react框架中，一旦state状态
发生改变，将会触发组件重新渲染，此时会先调用shouldComponentUpdate函数，若此函数返回false，则后续的
componentWillUpdate，render，componentDidUpdate均不会调用，即组件不会重新渲染；若返回true，则紧接着执行
componentWillUpdate，即在组件状态改变前，做一些其他工作，例如发出消息通知等，该函数不能更改state。
然后会进行render渲染，在渲染结束后，调用componentDidUpdate，可以在此函数内操作更新后的DOM。</p><p>通过上述代码，会发现componentWillReceiveProps和componentWillUnmount方法没有被调用，因为这两个
方法是在组件接受父组件传递过来的参数时以及从组件自身被移除时才会触发。将以上main.js进行修改：</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">// HelloWorld组件的render方法修改如下：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">render() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        console.log('render begin');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        return (&lt;div&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            &lt;h1&gt;Test component life time&lt;/h1&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            &lt;p&gt;{this.state.default_value}&lt;/p&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            &lt;p&gt;{this.props.props_value}&lt;/p&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        &lt;/div&gt;)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">// 同时增加一个新的MainHelloWorld组件：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">class MainHelloWorld extends React.Component {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    constructor(props) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        super(props);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        this.state = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            props_value: 'MainHelloWorld',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            unMount_value: false</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        };</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    changeValue() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        this.setState({</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            props_value: 'ChangedHelloWorld'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    unMount() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        this.setState({</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            unMount_value: !this.state.unMount_value</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    render() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        return (</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            &lt;div&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            &lt;p onClick={this.changeValue.bind(this)} &gt;changeValue click me!&lt;/p&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            &lt;p onClick={this.unMount.bind(this)} &gt;unMount click me!&lt;/p&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            {!this.state.unMount_value &amp;&amp; &lt;HelloWorld props_value={this.state.props_value}/&gt;}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            &lt;/div&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        )</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">// 修改ReactDOM.render方法：</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">ReactDOM.render(&lt;MainHelloWorld /&gt;, document.getElementById('content'));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>此时运行程序，首次刷新页面时，在控制台出现的信息和之前一致。
接下来点击页面”changeValue click me!“时，控制台出现以下信息：</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">componentWillReceiveProps:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">shouldComponentUpdate:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">componentWillUpdate:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">render begin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">componentDidUpdate:</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>由此可看出在组件接收参数更新时，基本与componentDidMount之后一致。
若点击页面”unMount click me!“，页面上关于HelloWorld组件的部分消失，控制台出现以下信息：</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">componentWillUnmount:</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>若再次点击页面”unMount click me!“，则会出现与首次加载页面一样的信息。
通过以上实际检测，绘出react组件的执行周期图如下：</p><p><img loading="lazy" alt="react" src="/assets/images/react-499eb25e1cd8c56e31f898a8b95fe73f.png" width="1151" height="1047" class="img_qnnJ"></p>]]></content>
        <category label="react" term="react"/>
        <category label="组件" term="组件"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[概率人生]]></title>
        <id>/2016/03/31/</id>
        <link href="https://www.bigtalk.xyz/blog/2016/03/31/"/>
        <updated>2016-03-31T05:38:24.000Z</updated>
        <summary type="html"><![CDATA[首次将概率与人生联想到一起，源于高中的一堂数学课上。当时老师在讲一些排列组合和概率相关的东西，我的思绪却早就随着目光穿过玻璃窗飘向了远方。那会儿想到生活若是由一系列概率事件组成，该是多么的令人沮丧；年少时总是过于遵从于内心，认为可以掌控一切。当时是如何都想象不到，那时的一个念想，会和今天的这篇文章产生联系，这不也是种概率事件么？]]></summary>
        <content type="html"><![CDATA[<p>首次将概率与人生联想到一起，源于高中的一堂数学课上。当时老师在讲一些排列组合和概率相关的东西，我的思绪却早就随着目光穿过玻璃窗飘向了远方。那会儿想到生活若是由一系列概率事件组成，该是多么的令人沮丧；年少时总是过于遵从于内心，认为可以掌控一切。当时是如何都想象不到，那时的一个念想，会和今天的这篇文章产生联系，这不也是种概率事件么？</p><p>生活中的一次学习，一次旅行，一次谈话，一次微不足道的选择，都有可能在未来掀起巨大的涟漪。背后主宰这一切的，无非就是一次次的概率事件：同样的分数，因为心仪的学校扩招，就进去了，反之则无缘；同样的提案，因为客户心情不好，而拒绝了；同样的路口，选择了不同的方向，走向差异化的人生。正因为这种种的不可控，可控的努力才会显得如此重要吧。</p><p>结识哪些人，经历什么故事，不过是一次次选择与概率共同作用的结果。现实中常常会着重强调选择与努力，选择性的忽略掉概率或者运气这回事。古人说的一命二运三风水四功德五读书，并非完全无道理。尽人事，听天命，有人认为这是对现实无奈的妥协，我却认为这是理解客观世界后的形成的一份豁达：人力总有穷尽，力有不逮时，顺其自然未尝不是件好事。得承认，有些事情，是你做不好的；有些高度，是你达不到的；有些努力，是会白费的。</p><p>人生本无所谓大、小抉择，任何一次的取舍，都有可能成为一个新的转折点：</p><div class="admonition admonition-tip alert alert--success"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="12" height="16" viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>语录</h5></div><div class="admonition-content"><p>当你老了，回顾一生，就会发觉：什么时候出国读书，什么时候决定做第一份职业、何时选定了对象而恋爱、什么时候结婚，其实都是命运的巨变。只是当时站在三岔路口，眼见风云千樯，你做出选择的那一日，在日记上，相当沉闷和平凡，当时还以为是生命中普通的一天。</p><p><strong>——陶杰 &nbsp;《杀鹌鹑的少女》</strong></p></div></div>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[测试那点事]]></title>
        <id>/2016/01/29/</id>
        <link href="https://www.bigtalk.xyz/blog/2016/01/29/"/>
        <updated>2016-01-29T12:19:07.000Z</updated>
        <summary type="html"><![CDATA[自开始参加工作到如今，已然有七八个年头了，突然发现在不同的公司，担任不同的职位，测试一直都占据工作内容中的相当大的一部分。从最初的新产品导入中的生产测试安排，治具制作，异常分析，到软硬件均有的开发测试，甚至存粹的硬件测试，采用过不同的工具，经历各种手法，体验各种流程，历经不同公司文化的洗礼。得出的结论是：无论一个公司宣传其对测试有多重视，流程有多规范，听听就好，切不可当真，特别是一些国内企业。这就好比熟人见面问吃了吗？仅仅是礼貌，当你回答没吃的时候很少会有人真请你吃饭的，如果真的有人请，那就是不可多得的好友了。]]></summary>
        <content type="html"><![CDATA[<p>自开始参加工作到如今，已然有七八个年头了，突然发现在不同的公司，担任不同的职位，测试一直都占据工作内容中的相当大的一部分。从最初的新产品导入中的生产测试安排，治具制作，异常分析，到软硬件均有的开发测试，甚至存粹的硬件测试，采用过不同的工具，经历各种手法，体验各种流程，历经不同公司文化的洗礼。得出的结论是：无论一个公司宣传其对测试有多重视，流程有多规范，听听就好，切不可当真，特别是一些国内企业。这就好比熟人见面问吃了吗？仅仅是礼貌，当你回答没吃的时候很少会有人真请你吃饭的，如果真的有人请，那就是不可多得的好友了。</p><p>形成这种观念绝不是个人的偏见，测试作为一种生产过程活动，目的是借助工具，通过各种手法发现产品存在的缺陷，以便直接来评判产品的质量。应该来说测试在整个生产活动中，是比较重要的一个环节，然而在实际中却是不受待见的。给人的感觉更多的是，因为大家都有或者迫于流程必须有测试这个环节，所以才会有测试这个职位的存在。这就好比小朋友的攀比心理，因为大家都有那个玩具，所以我也必须得有一个，至于喜不喜欢根本不重要。现实中常存在这种情况，一个项目进行到测试阶段，结果常常会以下状况：</p><p>当经测试发现很多问题的时候，通常无论是项目负责人还是开发第一反应就是是不是测试方法不对，工具使用不对，输入激励不对，当所有一一确认没问题后，通常还会考虑解决问题的难度，成本，问题放行的风险，直到最后觉得风险过大才会考虑去解决问题。这种行为模式在国内较为常见，既是能不改就不改，能放行就放行，这似乎已经成为一种通则。在以往工作中有参与到日本的一些产品项目，发现他们在对待问题的态度上则会严谨很多。对于所反馈的问题，他们基本不会怀疑你的专业性，通常会从源头开始寻找各种可能导致问题的原因，并寻找解决方案，哪怕是一些在常人看来无关紧要的问题，只要有人提出来，均会认真对待，不会因为问题是领导提出的就格外重视，因为问题是一个小小的操作工提出的而忽视。任何一种态度，最终都会反映在产品的品质上，这些应该是可以直观感受到的。</p><p>当经测试未发现什么问题的时候，通常会觉得是设计做得好，测试显得没什么作用；而一旦客诉出现没有被发现的问题是，此时测试则会被认为是无能了。这种做的好是应该，做不好就是无能的看法，在行业内普遍存在，当然不仅仅局限于测试岗位，相信很多其他职位也可一样。很多时候，这是一种无解的状态。</p><p>回到测试自身的工作状态来看，很多从事该职位的工作人员本身也就是混混而已，习惯于一成不变的遵循已有的标准，流程和规范。既不知道测试项目的意义，也不了解相关准则的由来，仅仅是因为大家一直都这样做没错，于是就理所当然的一切照搬。对于出现的问题也不加分析，没有个人见地，甚至连问题都无法描述清楚。在这样的情况下，难怪乎众人都会觉得测试是没什么技术含量的事情，因为当一个职业的大多数都表现得不够专业，你又如何让其他人相信你的专业呢。</p><p>当然，不仅仅是测试，在目前整个工作风气和环境中，几乎没有不憋屈的职位。研发设计被各种变更需求折磨，产品会觉得想法总不能被满足，运营总会觉得被各种资源限制。归根结底还是企业对于利润的追求过于痴迷，一切的手段都只是向钱看的时候，做产品的初心又有多少人还会记得呢？</p>]]></content>
        <category label="测试" term="测试"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[2015年终盘点]]></title>
        <id>/2015/12/31/</id>
        <link href="https://www.bigtalk.xyz/blog/2015/12/31/"/>
        <updated>2015-12-31T09:24:36.000Z</updated>
        <summary type="html"><![CDATA[转眼间又到了年终，一年的时间，三百多个日日夜夜，看似很长，却转瞬即逝。日复一日差不多的生活，也不会让人有太多的感触与回想。是时候盘点下这一年的生活了，权当记录一下，以免遗忘。]]></summary>
        <content type="html"><![CDATA[<p>转眼间又到了年终，一年的时间，三百多个日日夜夜，看似很长，却转瞬即逝。日复一日差不多的生活，也不会让人有太多的感触与回想。是时候盘点下这一年的生活了，权当记录一下，以免遗忘。</p><p>生活习惯方面，基本没什么大的变化，唯一值得一提的是戒烟的成功。很难想象抽了将近十年的烟说戒就戒，自己居然还一次性做到了。所以说很多事情，下定决心还是能做到的；太多的事情，都是在做之前就打退堂鼓，被臆想的难度给吓到。那些说习惯难改的，或许更多的是不愿下定决心，决心有多大，改变就可以有多大。</p><p>开通了个人空间，但全年下来才写了二十篇左右的文章，这点是很不满意的。就是刚开通那会一个月还能写几篇，到后面基本懒得更新，这方面的习惯还是得培养下，希望在明年能有大改变。</p><p>工作方面在两年的合同到期后没再签，结束两年的工作，自信在整个工作期间表现还算是不错，个人能力提升方面已然达到瓶颈。原本以为很快可以找到心仪的工作，现实却总会以打脸告终。所以说有自信是好的，有想法也是好的，但现实却不会因为这些对你有任何优待，环境形势总是比人强，这也算是被上了一课吧。</p><p>学习方面除了在工作之中的一些积累，在管理个人空间的过程中还顺带了解下php，算是达到了一个入门的菜鸟级别，同时尝试写了一个博客管理系统，虽然只是一个demo,起码一些基本功能算是完成了，自己还算满意。有时候想一想，如果当时不是进入了电子行业，或许会成为一个好的程序员呢。人生没有如果，就当其一个业余的爱好也挺好。</p><p>读书方面，今年大致看了十来本：《影响力》《大数据时代》《绝佳提问》《异类》《从0到1》《世界是平的》《新经济，新规则》《把生命浪费在美好的事物上》《追风筝的人》，同时还回顾了下《王小波文集》，基本上与互联网相关的占大多数，一方面了解现今的时代，同时也可以学习下新思想，对个人认知方面还是有一定的好处的。以后希望能保持阅读习惯。</p>]]></content>
    </entry>
    <entry>
        <title type="html"><![CDATA[站在风口的手机]]></title>
        <id>/2015/06/16/</id>
        <link href="https://www.bigtalk.xyz/blog/2015/06/16/"/>
        <updated>2015-06-16T16:52:25.000Z</updated>
        <summary type="html"><![CDATA[如果说二十一世纪的头十年有哪些事情一定会载入科技史的话，我想以下两点是必不可少的，一个是乔布斯推出了Iphone,另一个就是国产手机站到了互联网的风口，如同猪一般的飞了起来。如果乔老爷子没那么早离开的话，也许国产手机不会如现在这般造次。结果却是山中无老虎，猴子称大王，一堆流氓草寇揭杆而起，纷纷圈地站山为王。搞软件的做手机，搞教育的做手机，搞家电的做手机，搞影视的也做手机，有钱没地花的也来做手机。都在做能把东西做好也就罢了，偏偏又不思进取，把心思花在了互相之间勾心斗角上，除了互黑就是拉低底线。本来就是在互相抢食，只是在吃相上似乎有点难看。]]></summary>
        <content type="html"><![CDATA[<p>如果说二十一世纪的头十年有哪些事情一定会载入科技史的话，我想以下两点是必不可少的，一个是乔布斯推出了Iphone,另一个就是国产手机站到了互联网的风口，如同猪一般的飞了起来。如果乔老爷子没那么早离开的话，也许国产手机不会如现在这般造次。结果却是山中无老虎，猴子称大王，一堆流氓草寇揭杆而起，纷纷圈地站山为王。搞软件的做手机，搞教育的做手机，搞家电的做手机，搞影视的也做手机，有钱没地花的也来做手机。都在做能把东西做好也就罢了，偏偏又不思进取，把心思花在了互相之间勾心斗角上，除了互黑就是拉低底线。本来就是在互相抢食，只是在吃相上似乎有点难看。</p><p>手机本来只是用来通信和联络的，现在的手机无非就是屏幕大点，能上网，多了点传感器，装点应用外加玩玩游戏，不知道为什么这个Smart phone会被称为“智能手机”。这样的话干脆把电脑也叫做智能电脑好了。如果以目前的手机状态也能称得上智能的话，那智能也未免太过廉价，或者我们都是弱智？众多巨头扎堆手机市场，无非处于两个目的:一个觉得这个钱好赚，可以捞上一笔；另一个觉得这是一个很好的入口，赔本也得上。至于什么极致，情怀，或是创新的噱头，估计把这话说出口的那些人自己都不信。遗憾的是，仅仅为赚钱或者是入口，也可能会落空。</p><p>想想功能机时代的诺基亚，摩托罗拉，黑莓，以及众多其他国产手机和早期的HTC,这么多巨头都倒在了所谓智能手机的风口，凭什么你们这些连制造经验都没有，仅仅靠着一点小聪明就想占领市场？互联网思维再怎么勇猛总得有个根基才能站稳吧，不然的话一个漂浮的海市蜃楼有啥用。没自己的操作系统，没自己的器件，没自己的工艺，除了有自己廉价的劳动力拼拼装装，又有什么点是值得消费者非买不可的？于是只剩低价，比山寨还便宜的价格，杀敌一千自损八百。广告和推广作用是有限的，一旦被忽悠的群体反应过来，只有接受被消费者抛弃的命运。</p><p>关于入口这一说，其实是靠不住的。不知道为什么那么多精明的企业家会被这种说法给迷惑而深信不疑，难道就真的这么缺少思考能力？可能是因为当前手机的各种应用还算方便，用手指在屏幕上点击下就可以完成很多事情，于是就自然而然的想把所有的东西放到手机里，接着就更理所当然的会把手机当做一切的入口。意淫着用手机开门，开空调，开车，付账，做各种事只要点下屏幕，多酷啊，谁还能说手机不是入口。可是问题是我为什么要用手机啊，要是手机被取代了呢？</p><p>不要觉得手机被取代不可能，如果一个耳机模块就可以实现通话，一副眼镜或是美瞳就可以当显示器，为什么还要随身揣一个手机？信息传递的方式与形态一直在变，为什么要将思维局限在手机上？功能机从普及到现在基本消失也不过十来年，谁敢肯定下一个十年后还会不会有手机？既然连手机都可能不存在，还谈什么将手机当做入口，这不是无稽之谈么。</p><p>信息的入口一直都在变化，从信件到报纸到电视到网络，载体也一直在变，唯一永恒的只有信息的内容本身。没有强健的内容作为支撑，一切的入口不过是个花架子。所以奉劝一句，不管是在建入口还是在建生态圈，先把内容丰富起来吧，其他的一切不过是浮云。</p>]]></content>
        <category label="手机，互联网" term="手机，互联网"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[读《绝佳提问》]]></title>
        <id>/2015/06/14/</id>
        <link href="https://www.bigtalk.xyz/blog/2015/06/14/"/>
        <updated>2015-06-14T15:56:44.000Z</updated>
        <summary type="html"><![CDATA[《绝佳提问》是英文原著《A More Beautiful Question: The Power of Inquiry to SparkBreakthrough Ideas》的中文译本，作者Warren Berger。毫无疑问，这是一本很棒的有关商业创新的著作。一部优秀的作品，很多时候不需要拥有多高深的概念，也不需要有多华丽的词藻与论证，只需通过简单的论述，将原本被大众所忽视的提问价值，再一次发掘出来，就能让人读起来舒服且信服其观点。这不仅仅是一本商业著作，了解提问的艺术，对于生活或是工作，也是很有裨益的。]]></summary>
        <content type="html"><![CDATA[<p>《绝佳提问》是英文原著《A More Beautiful Question: The Power of Inquiry to SparkBreakthrough Ideas》的中文译本，作者Warren Berger。毫无疑问，这是一本很棒的有关商业创新的著作。一部优秀的作品，很多时候不需要拥有多高深的概念，也不需要有多华丽的词藻与论证，只需通过简单的论述，将原本被大众所忽视的提问价值，再一次发掘出来，就能让人读起来舒服且信服其观点。这不仅仅是一本商业著作，了解提问的艺术，对于生活或是工作，也是很有裨益的。</p><p><strong>未来最需要的不是知道答案的人，而是能够提出美丽问题的人。</strong>粗看起来是一种很颠覆的观点，仔细一想，就会发现，任何的答案都是为解决某一个问题而存在。从来就只有没有答案的问题，却没有没有问题的答案。一直以来，我们都忽视了提问的重要性，总是目光短浅的盯着答案。须不知答案均是对应于问题的，从某个方面来说，人类世界进程是由一系列的提问所促进也不为过。</p><p>连续的探寻式提问，可以有助于揭露事物的本质，从而找到最优的解决方案。遗憾的是，随着年龄的增长以及被社交社会的同化，大部分人都丧失了提问的能力，或者说是创新的能力。一直以来，在整个的学习以及社会环境中，都是被教导要记住答案的，要守规矩的，很少有遇到有人教你怎么提问，怎么思考。最常见的就是，这都是为你好，听我的没错，你这样做才是对的。结果往往是一边喊着要创新一边又亲手将之谋杀。</p><p>如何提出有艺术的问题？作者给出的是要保持好奇心，像孩子般对所有事物和行为的好奇，同时承认自身的不足。偏偏这两者都是成年人缺少的。一方面是忙于生活和工作，很少有空闲去想为什么，对不对以及该如何做等问题；另一方面，碍于面子或是其他因素，越是高管与行业专家，越是难以承认自身的不足，耻于询问是当代人的通病。这似乎是种恶性循环，唯有打破这层行为和心里的障碍，才是良方。</p><p>提问的能力是需要培养的。有种说法是哪里有抱怨，哪里就有商机。人们常常抱怨的地方，就是问题所在。只是通常情况下被大众所忽略。越是习以为常的东西或是行为，越要对其保持警惕，因为问题可能就掩盖在表象之下。锻炼自己的敏锐触觉，抽出时间进行反向思考，如果不那么做会怎样？同时扩充自己知识的边界，提高眼界，也许人人都能成为可以提出优秀问题的人。</p><p>以前，一直喜欢从他人那里寻求答案，人就是这么奇怪，遇到不确定问题的时候，总希望有人能指明道路，相信他人远超过自己。直到活到而立之年，才发现生活中没人能给你答案，探寻与尝试本是人生的常态。提出自己的问题，确定方向，解决它，这才是正常之道。</p>]]></content>
        <category label="提问" term="提问"/>
        <category label="创新" term="创新"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[谁制造了垃圾产品]]></title>
        <id>/2015/06/08/</id>
        <link href="https://www.bigtalk.xyz/blog/2015/06/08/"/>
        <updated>2015-06-08T05:18:59.000Z</updated>
        <summary type="html"><![CDATA[对于产品，个人更倾向于将其分为产和品两个部分。产则是品质，品牌，品味。这二者的结合，才可以称为完整意义上的产品。从业至今将近十年，直接参与生产的产品不下十种，生活中接触到的产品，更是不下千余种，最大的感触就是制造了一堆垃圾。]]></summary>
        <content type="html"><![CDATA[<p>对于产品，个人更倾向于将其分为产和品两个部分。产:表现为设计，生产制造，推广销售以及使用等；品:则是品质，品牌，品味。这二者的结合，才可以称为完整意义上的产品。从业至今将近十年，直接参与生产的产品不下十种，生活中接触到的产品，更是不下千余种，最大的感触就是:符合真正意义上的产品为数不多，绝大部分仅仅停留在“产”的阶段，与“品”还差十万八千里。引用乔布斯的话就是:制造了一堆垃圾。</p><p>可能有人会觉得这样的想法太过偏激，放着几十年前也许是，因为那时物质极其缺乏，物质的需求尚难以满足，更难提及美感要求。但从十九世纪末的第二次工业革命至今，经历一百多年的产品快速发展，需求，也早已超越物质本身。现如今的很多所谓产品根本就配不上这个时代，以至于在苹果只是推出了符合现代的产品，却一下子惊艳了世人。很多时候不是因为他有那么优秀，而是我们的欣赏水平被垃圾产品拉得太低，以至于连正常的也觉得惊艳。反思下不难看出，现世的绝大部分东西是需要从新设计的。</p><p>对于创新，一直是广大企业家所追求的。从近年来常说的什么重新定义了手机，重新定义了汽车以及所谓的互联网思维等，均可看出对创新的渴求。然而国内通常越是缺什么就越会宣传什么，很难想象一些骨子里还都是处在工业时代思维的人，如何去引领互联网时代的创新。如果说工业时代推翻了封建思想，那么互联网时代必须推翻工业时代的余毒，才有可能将生产由“产”带入“品”的时代。当一种模式持久没有突破，说明必定存在先天的缺陷。个人以为工业时代的以下几点是急待突破的。</p><p><strong>废除雇用制</strong>
现行的雇用制应该是工业时代的一个标志，通过合同来约束资本方和劳力方(下文以公司和员工代替劳资双方。)，公司提供场地，设备，付薪水，员工提供脑力或体力完成工作获取相应的报酬。虽说合同在一定程度上保证了劳资双方的公平，但公司实际总处于优势地位，它不但完全的占有员工合同里规定的工作时间，甚至在雇用期间员工的所有发明专利均会被划给公司(大部分入职合同里都会有类似的说明，和一些保密，行业竞争等协议)。至于一些绩效考核，升职加薪常常都是公司说了算，所处地位的不平等必然导致心里的不满，容易行成消极怠工。再一个就是工业时代每个员工分工细致，企业更多的是需要员工符合标准的执行而非异想天开的想法创意，一旦生产活动成为了一个流水线，产品的质量就会由最差的环节决定。传统的雇用制是没法让一个公司招聘到的全部员工都是优秀的，总会有一些滥竽充数者拉低公司的水平，导致垃圾产品的产出。</p><p>那么不采用雇用制该怎么办呢？答案是变为项目合作制。公司只是作为一个法律上的载体，名义上只有公司项目，没有公司。公司负责出资金，场地设备作为资本入股，项目发起人物色需要的具备相应技能的项目成员，作为技术入股。项目成员间均是合作关系，没有上下级与雇用关系的存在。一个项目成员可以同时参与不同公司的不同项目，一个公司也可以同时投资不同项目并与不同组员合作。成员付出与获利均由项目组自己商讨决定并拟定合同作为规范。这才是符合创新的基本条件:让最优秀的人聚集一起做成功的事情。(现今有一些电影其实也是采取类似的合作方式，只是为什么产出的还是垃圾电影居多，就不在本文的讨论范围内了。)</p><p><strong>团队要小，流程应短</strong>
通常一个人能完成的工作，更容易创造出流世的金典之作。例如诗人，画家，作家，运动员，作词作曲家，演唱演讲家等。个人活动能完成的工作成果，远比团体合作完成的金典要多。受限于个人项目的，只有天赋和努力，而对于团体，木桶理论往往更致命。</p><p><strong>消除等级，扁平管理</strong>
传统的公司组织架构里，实行等级制度，一般为懂事长，总经理，部门经理，组长，普通员工的分层方式，典型的金字塔结构。话语权与决策权也是从高到低，使公司变成了少数人指挥大多数人行为及方向的地方。若遇到出类拔萃的领导人，比方式无可厚非。而现实中，绝大多数领导人并非天赋异禀，智商及决策能力也非超出大众很多(相信我，大部分混到领导阶层的人，运气和环境成分居多，能力方面未必就超出大众。这种观点可能许多人都无法接受，不管是大众还是那些领导人，大部分都会觉得领导总是比较有长远的眼光以及卓越的决策能力。其实换做任何一个正常人，能在相同的位置接受同样多的资讯，作出相同决策的可能性极大。)，一旦作出某些错误的指示，结果就可想而知。工作中总会遇到类似的领导，拿他们多年积累的一些经验要求所要人服从，结果总是弄得一团糟，还埋怨手下无能。</p><p>扁平化是不再设置权利，决策需讨论以及以理服人。一个领导人，若连按自己的想法说服员工都做不到的话，我想也不会是卓越的领导。在国内，大众对权利一直是敬畏的，通常不会表达一些真实的想法与建议。这无疑会导致创意的流失。只有去除特权和阶层，在扁平化看似没有领导的环境里，其实每个人都可以成为某些创意的领导，创新与有品味的产品才会比较容易诞生。</p><p><strong>追求极致，拒绝妥协</strong></p><p>社会上有种比较流行的说法是:见人说人话，见鬼说鬼话。宗旨无非是教人在社会上要圆滑世故，适当的做些妥协。此种处世哲学在社会生活中是否适用暂不讨论，若以此行为指导产品生成过程，必然导致垃圾的产出。想象一下，假若一个产品从需求规划之初，到设计生产，到最终交付，总共经历100个环节，每个环节允许千分之一的妥协，那么最终的产品的完整度有多少呢？答案是只有最初的90.48%。这种情况在真实世界是常常在发生的，各环节负责人都觉得自己已经做得够好了，结果却是差强人意，你若是老板，会被那千分之一蒙蔽吗？</p><p><strong>跳出专业的井底，适当跨界</strong></p><p>明确的分工制度是符合工业时代需求的。各职位与技能都像固定设备的螺丝一样一一对应，工作行为上的单一会在潜意识里导致思维的僵化，一旦眼界受限，则很难看到界限之外的景色。跨界，是很好避免思维单一的方法，应用好不同行业的模式，常常是带来创意比较有效的手段。</p><p>很多时候，我们会在无意间做出与期望相去甚远的东西，了解背后的原因，才能有利于提升并激发新的创意。</p>]]></content>
        <category label="产品" term="产品"/>
        <category label="设计" term="设计"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[python装饰器]]></title>
        <id>/2015/06/02/</id>
        <link href="https://www.bigtalk.xyz/blog/2015/06/02/"/>
        <updated>2015-06-02T13:43:41.000Z</updated>
        <summary type="html"><![CDATA[在看python相关教程时，碰到介绍装饰器(decorator）相关章节很费了一番脑筋，不知道是因为翻译不达意还是个人理解偏差的缘故，总是感觉难以理解。有的教程上说装饰器在不改变原始函数(方法)和类的情况下对其进行装饰功能，本身是一个返回可调用对象的可调用对象。这个说法显得有些绕口，在python中，常用的可调用对象基本是function和class，这就意味着装饰器本身可以为二者之一；对于方法和类，调用时可接受一些参数，而针对装饰器，则对传入的参数有一定的限定：方法或类，并在内部做完相关的工作后，返回对应的方法或类。]]></summary>
        <content type="html"><![CDATA[<p>在看python相关教程时，碰到介绍装饰器(decorator）相关章节很费了一番脑筋，不知道是因为翻译不达意还是个人理解偏差的缘故，总是感觉难以理解。有的教程上说<strong>装饰器在不改变原始函数(方法)和类的情况下对其进行装饰功能，本身是一个返回可调用对象的可调用对象</strong>。这个说法显得有些绕口，在python中，常用的可调用对象基本是function和class，这就意味着装饰器本身可以为二者之一；对于方法和类，调用时可接受一些参数，而针对装饰器，则对传入的参数有一定的限定：方法或类，并在内部做完相关的工作后，返回对应的方法或类。</p><p>例如，想知道某些函数的调用情况，可以定义一个counter的函数装饰器，记录对应的调用次数，然后返回原函数：</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; count=0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; def counter(func):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...     def counternumber(*args,**kwargs):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             global count</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             count+=1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             print("The %s times to %s" %(count,func.__name__))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             return func(*args,**kwargs)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...     return counternumber</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; @counter</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">... def sum(x,y):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...     print(x+y)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; sum(1,2)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">The 1 times to sum</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">3</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; sum(3,4)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">The 2 times to sum</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">7</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; @counter</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">... def multiply(x,y):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...     print(x*y)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; multiply(1,2)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">The 3 times to multiply</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; multiply(4,5)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">The 4 times to multiply</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">20</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>以上代码开看起来是实现了相关功能，但由于装饰器内部的counternumber函数存在，导致改变了被装饰函数的函数名，此部分相信是大部分人不愿见到的。</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; print(sum.__name__)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">counternumber</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; multiply.__name__</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">'counternumber'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt;</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>可以看到，所有被装饰的函数名均被替换成了装饰器内部的函数。那么，有没有办法可以不改变被装饰的函数名呢，答案是肯定的。参见如下示例代码：</p><div class="codeBlockContainer_nHJp theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_Y1rG"><pre tabindex="0" class="prism-code language-text codeBlock_aREz thin-scrollbar"><code class="codeBlockLines_J5fJ"><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; class counter:</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...     def __init__(self,func):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             self.func=func</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             self.__name__=func.__name__</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             self.count=0</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...     def __call__(self,*args,**kwargs):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             self.count+=1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             print("The %s times to %s" %(self.count,self.__name__))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...             return self.func(*args,**kwargs)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; @counter</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">... def f(x):</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...     return x</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; f(1)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">The 1 times to f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">1</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; f('fun')</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">The 2 times to f</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">'fun'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt; f.__name__</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">'f'</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&gt;&gt;&gt;</span><br></span></code></pre><div class="buttonGroup_YmO7"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_deH0" aria-hidden="true"><svg class="copyButtonIcon_yeat" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_Neru" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>这次的counter是一个类，但可以用来装饰函数f，在常规用法中，装饰器本身可以是方法或类，被装饰的对象也可以是方法或类。带来好处是可以在不影响被装饰对象的情况下，附着一些其他功能。</p><p>另：在Python内置的functools.wraps可以解决被装饰对象属性变化的此类问题，此处不再详细介绍。</p>]]></content>
        <category label="python" term="python"/>
        <category label="装饰器" term="装饰器"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[思考的能力]]></title>
        <id>/2015/05/26/</id>
        <link href="https://www.bigtalk.xyz/blog/2015/05/26/"/>
        <updated>2015-05-26T12:48:54.000Z</updated>
        <summary type="html"><![CDATA[“你有没有什么观点，是自己认为对的，却一直不被其他人所接受？”，第一次看到这句话的时候，仔细想了想，意识里好像真没什么观点有这么特别。所有闪现的都是众人所认可的价值观：要热爱学习，努力工作，尊老爱幼，诚实勤勉，宽以待人等。突然的感觉到莫名的恐惧，是的，这是很可怕的事情，当然不是说这些价值观可怕，无论是过去还是现在，甚至在可预见长远的未来，这些价值观的存在都是合理的。没必要去讨论这些价值观的对错，那很容易将人引入哲学的范畴。我说的可怕是当你所有的观点，想法，行为都与大众一致的时候，你怎么证明自己是独特的存在而不是一个复制品？你又怎么能证明所有这些认可的观点，是因为自己思考后觉得正确，不是受环境影响，而被动接受认为的正确。]]></summary>
        <content type="html"><![CDATA[<p>“你有没有什么观点，是自己认为对的，却一直不被其他人所接受？”，第一次看到这句话的时候，仔细想了想，意识里好像真没什么观点有这么特别。所有闪现的都是众人所认可的价值观：要热爱学习，努力工作，尊老爱幼，诚实勤勉，宽以待人等。突然的感觉到莫名的恐惧，是的，这是很可怕的事情，当然不是说这些价值观可怕，无论是过去还是现在，甚至在可预见长远的未来，这些价值观的存在都是合理的。没必要去讨论这些价值观的对错，那很容易将人引入哲学的范畴。我说的可怕是当你所有的观点，想法，行为都与大众一致的时候，你怎么证明自己是独特的存在而不是一个复制品？你又怎么能证明所有这些认可的观点，是因为自己思考后觉得正确，不是受环境影响，而被动接受认为的正确。</p><p>不要觉得纠结于观点的来源不重要，思考所得与被动接受是完全不同的过程，这就好比编程中的递归函数与循环函数一样，虽然在很多时候二者产生的结果都一样，却有着不同的开销与效率。对于被动接受的观点及思维，在很大程度上会影响思考的能力，甚至抹杀思考的能力，并且让人觉得所思所为均是个人思考的结果，因为你无法用一个深受周遭环境影响的思维证明自己的思维不受影响，就好比一个精神病人无法证明自己没有患精神病一样。从很大一定程度上来说，用大众的行为和思考方式，是无法说明一个人具备思考能力的。只有从反常的行为和异于一般的观点，才能很好的证明个人的思考能力，代价就是要么成为天才，要么被众人认为是神经病。</p><p>之所以思考这些，是个人一直没意识到缺少思考的能力，当自己的思想经不起思考后，这无疑是一种灾难。缺少思考的能力，才会导致所有的随波逐流，一旦如此，个人的价值如何体现？就好像拥有两台一样操作系统、应用程序以及数据的电脑，其信息的价值根本不比一台多。作为一个有血有肉的个体，相信没有谁愿意在这一生的短短几十年里，仅仅复制了一些他人的想法与观念后GAME OVER。</p><p>培养思考能力的方法只有思考本身，同时警惕一切大众的思想与做法。众人常是缺少思考能力的，看看中世纪时期天主教会统治下的民众，希特勒时期的纳粹分子，你会发现民众的思想可以僵固到没有底限。也许你会庆幸现世的开明，但我可以明确的告诉你的是，在不远的将来，看待我们的今天与我们今天看待中世纪是没任何区别的。在任何时代，真理总是掌握在少数人手中的；当大部分人宣称掌握了某项真理的时候，要么是盲从，要么是被忽悠，此时是好好反思这些真理的时候。</p><p>现代的社会和教育，往往是抑制思考能力的。仔细想想你的生活，学习时使用的教材是一些年纪比你大很多的人，根据他们的经验编写的；他人给你的经验与劝告也是基于已经经历过的或者是另外一些人传授的；了解到的技艺也是流传下来的。发现没，所有的来源均是过去式，而我们活在现在。没任何人可以保证基于过去的经验能完全适用于现在甚至是将来，因此你若全盘接受，必定成为随大流的一员。而一味的灌输，是现在社会和教育最擅长的事情。唯有自己思考，才是唯一的出路。这就是为什么创新来之不易。真正优秀的人，早已跳出大流，他们会分享他们的经验，但仅能参考，你若全盘照搬，则又将陷入随大流的情景。</p><p>对于很多人来说，一辈子都在随大流，不必思考大多，也不觉得有什么不妥。确实，随大流也可以过上安稳舒适的日子，道德上也被主流认可，一样美满幸福。推动世界进程的，永远是少数。思考，从现在开始，哪怕得出错误的结果，或者被思考。</p>]]></content>
        <category label="随笔" term="随笔"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[世界是平的，道路是曲折的]]></title>
        <id>/2015/05/11/</id>
        <link href="https://www.bigtalk.xyz/blog/2015/05/11/"/>
        <updated>2015-05-11T14:10:01.000Z</updated>
        <summary type="html"><![CDATA[读书笔记]]></summary>
        <content type="html"><![CDATA[<p>最近阅读托马斯.弗里德曼的畅销著作《世界是平的—21世纪简史》，作者将最近二十年的全球化进程，对比成世界越来越平坦。得益于科技的快速发展，导致近些年信息的快速流通，越来越便捷的交通，以及逐渐平常化的跨地域商业经济活动，无一不印证着全球一体化的趋势。这种全球化的进程冲击着各国的经济，政治和文化，使得机遇与挑战并存。</p><p>对于生活在当下的人来说，都在逐渐习惯这种全球化的格局，特别是近几年发展迅速的电商，使得世界各地的商品可以迅速的流通。大型企业也越来越不满足仅仅获取本土的利益，更多的参与到全球市场的行列。很多人可能有这种意识，但又不是十分明朗，在通读此书后，会让你对作者的叙述产生十分认同的感觉。不得不佩服作者深厚的叙事功底，通过其自身对一些跨国集团的访问，结合当前互联网的发展，以及国家政治和经济环境，精彩的讲述全球化进程。</p><p>作者显然对平坦化进程持乐观态度，一方面阐述企业外包与经济合作对第三世界国家带来的利益，一方面谈及因工作外包导致对美国低端工作人员的影响，并引用中国和印度经济的崛起作为例子，呼吁美国加强教育和科技创新方面的投资，以保持其世界的领先地位。从这方面看，作者倒是像一个布道者，不知道是有意还是疏漏，对于平坦化进程所描述的基本都是积极层面的东西，对于不利的方面就鲜有提及，个人认为是作者的故意为之。书中对中国，印度等国家经济崛起可能对美国的影响的描述有夸大嫌疑，特别是对中国现状的一些描述明显是不符合实情的，而作者作为《纽约时报》的专栏作家，对于信息层面东西的获取不可能如此浅显，只能认为是作者的故意为之，目的无非就是想警醒美国当局不可大意，要保持持续的创新以保持其世界霸主的地位不可动摇。至于文中提到的经济合作会带来和平，这是比较幼稚的观点，从来经济只是为政治服务的，想要通过经济来左右政治的行为，大概只要有人存在的地方就不大可能吧。</p><p>任何事物都有双面性，不能只仅仅看表面。在平坦的世界里，发达国家将一部分低端产业或者低附加值的商业活动转移到欠发达地区，表面上看对发达国家就业形成一定的影响，但考虑到成本的降低以及其获得的利益，其总体收益绝对是远远大于负面影响的。就以美国制造业来说，其国内愿意成为生产线工人的数量绝对得是满足不了其强大的工业生产能力的。像著名的苹果，IBM，惠普等企业，其整个供应链上的工人都有成百上千万之众。因此一个企业是否需要跨国生产，不仅仅是一个人力成本的问题，背后还有其它的利益相关。商业世界，从来没有所谓的共赢，当两个企业实现所谓的共赢后，必然有大量的消费者为其买单。</p><p>至于那些接受低端产业转移的国家，像中国，印度等，表面上看，改善了就业形势，似乎也在一定程度上实现了人民收入的增长。但因市场开放，收入在很大程度上抵不过物价的上涨，同时因为这类工作岗位的增加，导致教育导向跟着一起变化，培养了大量廉价的操作工，技术员，很难评价这样的代价是否值得。因为处于供应链的末端，很难接触到高新技术，极其容易触碰到上升空间的天花板；另一方面，初级加工及生产常常伴随大量的资源浪费以及环境污染。因此接受发达国家的低端产业，是否如作者书中写到对发展中国家有那么大的帮助，实在值得思考。</p><p>全球化不光是资本之间的博弈，更是国家政治间的博弈，文化之间的博弈。世界变得越来越平坦是无法阻挡的，但平坦不能带来公平，也无法实现公平。在前期，发达国家已经占尽上风，在全球化进程中，通过其科技实力已经在对发展中国家进行掠夺。若发展中国家仅仅被动接受其施舍的全球化，则难以逆袭；在经历一番饮鸩止渴后，唯有科技创新，才是平坦世界的唯一出路。</p>]]></content>
        <category label="思考" term="思考"/>
        <category label="读书" term="读书"/>
    </entry>
</feed>