

<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>tornado &#8211; Max的程式語言筆記</title>
	<atom:link href="https://stackoverflow.max-everyday.com/tag/tornado/feed/" rel="self" type="application/rss+xml" />
	<link>https://stackoverflow.max-everyday.com</link>
	<description>我要當一個豬頭，快樂過每一天</description>
	<lastBuildDate>Wed, 03 Jan 2024 11:13:17 +0000</lastBuildDate>
	<language>zh-TW</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>

<image>
	<url>https://stackoverflow.max-everyday.com/wp-content/uploads/2017/02/max-stackoverflow-256.png</url>
	<title>tornado &#8211; Max的程式語言筆記</title>
	<link>https://stackoverflow.max-everyday.com</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>在tornado做出網址即為資料的id的handler</title>
		<link>https://stackoverflow.max-everyday.com/2023/02/tornado-id-handler/</link>
					<comments>https://stackoverflow.max-everyday.com/2023/02/tornado-id-handler/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 22 Feb 2023 09:05:36 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=4383</guid>

					<description><![CDATA[這個需求只用一句話講滿抽象的, 以觀看還有編輯 ...]]></description>
										<content:encoded><![CDATA[
<p>這個需求只用一句話講滿抽象的, 以觀看還有編輯 youtube 影片為例, 影片的 id 就在網址裡. </p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="525" height="208" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-22-17_00_47-影片詳細資料-YouTube-Studio.jpg" alt="" class="wp-image-4385"/></figure>



<p></p>



<p>也可以參考看看 tornado 的 blog 範例, 他的  router 下法:</p>



<pre class="wp-block-code"><code>class Application(tornado.web.Application):
    def __init__(self, db):
        self.db = db
        handlers = &#91;
            (r"/", HomeHandler),
            (r"/archive", ArchiveHandler),
            (r"/feed", FeedHandler),
            (r"/entry/(&#91;^/]+)", EntryHandler),
            (r"/compose", ComposeHandler),
            (r"/auth/create", AuthCreateHandler),
            (r"/auth/login", AuthLoginHandler),
            (r"/auth/logout", AuthLogoutHandler),
        ]
</code></pre>



<p></p>



<p>在 EntryHandler 裡, 存取 slug 就可以拿到 url 裡的我們要的 id.</p>



<pre class="wp-block-code"><code>class EntryHandler(BaseHandler):
    async def get(self, slug):
        entry = await self.queryone("SELECT * FROM entries WHERE slug = %s", slug)
        if not entry:
            raise tornado.web.HTTPError(404)
        self.render("entry.html", entry=entry)
</code></pre>



<p></p>



<p>如果確定是拿 /&#8230;/edit 的, 也可以設定成這樣子:</p>



<pre class="wp-block-code"><code>(r'/your-object/(.*)/edit', 'app.controller.ObjectEditorHandler'),</code></pre>



<p></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2023/02/tornado-id-handler/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tornado 也是最好用的輕量級 web server</title>
		<link>https://stackoverflow.max-everyday.com/2023/02/404-while-using-tornado-staticfilehandler-class/</link>
					<comments>https://stackoverflow.max-everyday.com/2023/02/404-while-using-tornado-staticfilehandler-class/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Fri, 17 Feb 2023 07:12:12 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=4350</guid>

					<description><![CDATA[跟 Flask 相比, tornado 似乎討論...]]></description>
										<content:encoded><![CDATA[
<p>跟 Flask 相比, tornado 似乎討論的文章比較少, 但Max最喜歡使用的 web server 是 tornado, 今天來介紹 tornado 如何與 Flask 一樣, 掛載靜態檔案. </p>



<p>Flash 的使用方法, 真的很直覺, 程式碼也超短:<br><a href="https://stackoverflow.max-everyday.com/2023/02/flask-web-server-static-file/">https://stackoverflow.max-everyday.com/2023/02/flask-web-server-static-file/</a></p>



<p>Tornado 對靜態檔案的說明:<br><a href="https://www.tornadoweb.org/en/stable/web.html#tornado.web.StaticFileHandler">https://www.tornadoweb.org/en/stable/web.html#tornado.web.StaticFileHandler</a></p>



<p>使用下面的範例, 就可以存取到靜態檔案:</p>



<pre class="wp-block-code"><code>import asyncio
import tornado
from tornado.web import Application
from tornado.web import StaticFileHandler

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return Application(&#91;
        (r"/", MainHandler),
        (r"/content/(.*)", StaticFileHandler, {"path": "./content"}),
    ])

async def main():
    app = make_app()
    app.listen(8888)
    await asyncio.Event().wait()

if __name__ == "__main__":
    asyncio.run(main())</code></pre>



<p>說明: ./content/ 輸出網頁的 /content/</p>



<p>使用的執行範例:<br>http://127.0.0.1:8888/content/static.py</p>



<figure class="wp-block-image size-full"><img decoding="async" width="558" height="444" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-14_49_15-127.0.0.1_8888_content_static.py_.jpg" alt="" class="wp-image-4351"/></figure>



<p></p>



<p>有一個奇怪的坑是, 如果你在 create Application(routes, **app_settings) object 時, 所帶入的 app_settings = dict( ) 中間使用 static_path, 例如:</p>



<pre class="wp-block-code"><code>app_settings = dict(
    template_path=os.path.join(os.path.dirname(__file__), "templates"),
    static_path=os.path.join(os.path.dirname(__file__), "static"),</code></pre>



<p>這時候, 又在 routes 裡設了 static, 例如:</p>



<pre class="wp-block-code"><code>handlers = &#91;
        (r'/', MainHandler),
        (r'/static/(.*)', StaticFileHandler, {'path': "static"}),
    ]</code></pre>



<p>2邊就會衝突, 原本應該正常顯示的靜態檔案都會成錯誤訊息:</p>



<pre class="wp-block-code"><code>Traceback (most recent call last):
  File "D:\Programs\Python\Python39-32\lib\site-packages\tornado\web.py", line 1713, in _execute
    result = await result
  File "D:\Programs\Python\Python39-32\lib\site-packages\tornado\web.py", line 2611, in get
    self.absolute_path = self.validate_absolute_path(self.root, absolute_path)
  File "D:\Programs\Python\Python39-32\lib\site-packages\tornado\web.py", line 2813, in validate_absolute_path
    raise HTTPError(404)
tornado.web.HTTPError: HTTP 404: Not Found</code></pre>



<figure class="wp-block-image size-full"><img decoding="async" width="817" height="229" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-15_08_45-https___127.0.0.1_5000_static_components.json_.jpg" alt="" class="wp-image-4352" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-15_08_45-https___127.0.0.1_5000_static_components.json_.jpg?v=1676617750 817w, https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-15_08_45-https___127.0.0.1_5000_static_components.json_-600x168.jpg?v=1676617750 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-17-15_08_45-https___127.0.0.1_5000_static_components.json_-768x215.jpg?v=1676617750 768w" sizes="(max-width: 817px) 100vw, 817px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p>根目錄以下到 /static/目錄下取檔案, 並針對 404 不存在的檔案做客製化:</p>



<pre class="wp-block-code"><code>class MyStaticFileHandler(StaticFileHandler):
    def write_error(self, status_code, *args, **kwargs):
        my_404_page = os.path.join(options.app_root, 'template/error_404.html')
        self.set_status(status_code)
        if status_code in &#91;404]:
            self.render(my_404_page)
        else:
            self.write("Error: %d" % status_code)

handlers = &#91;
    (r'/', 'app.controller.HomeHandler'),
    (r'/(.*)', MyStaticFileHandler, {'path': os.path.join(app_root, 'static/')}),
]</code></pre>



<p> </p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2023/02/404-while-using-tornado-staticfilehandler-class/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tornado &#8211; &#8216;_xsrf&#8217; argument missing from POST</title>
		<link>https://stackoverflow.max-everyday.com/2023/02/tornado-_xsrf-argument-missing-from-post/</link>
					<comments>https://stackoverflow.max-everyday.com/2023/02/tornado-_xsrf-argument-missing-from-post/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 16 Feb 2023 10:26:49 +0000</pubDate>
				<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">https://stackoverflow.max-everyday.com/?p=4346</guid>

					<description><![CDATA[我的 web server 在接收到 post ...]]></description>
										<content:encoded><![CDATA[
<p>我的 web server 在接收到 post 時會失敗, 並顯示錯誤訊息:</p>



<pre class="wp-block-preformatted"> '_xsrf' argument missing from POST</pre>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="829" height="86" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-16-18_15_52-系統管理員_-C__Windows_system32_cmd.exe-python-workflowserver.py_.jpg" alt="" class="wp-image-4347" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-16-18_15_52-系統管理員_-C__Windows_system32_cmd.exe-python-workflowserver.py_.jpg?v=1676542607 829w, https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-16-18_15_52-系統管理員_-C__Windows_system32_cmd.exe-python-workflowserver.py_-600x62.jpg?v=1676542607 600w, https://stackoverflow.max-everyday.com/wp-content/uploads/2023/02/2023-02-16-18_15_52-系統管理員_-C__Windows_system32_cmd.exe-python-workflowserver.py_-768x80.jpg?v=1676542607 768w" sizes="(max-width: 829px) 100vw, 829px" /></figure>



<p>解法:<br><a href="https://stackoverflow.com/questions/12890105/tornado-xsrf-argument-missing-from-post">https://stackoverflow.com/questions/12890105/tornado-xsrf-argument-missing-from-post</a></p>



<p>I imagine you have Cross-site request forgery cookies enabled in your settings (by default it is on).</p>



<p><a href="http://tornado.readthedocs.org/en/latest/guide/security.html#cross-site-request-forgery-protection">Tornado&#8217;s XSRF is here</a></p>



<p>To fix this turn it off in your settings:</p>



<pre class="wp-block-code"><code>settings = {
    "xsrf_cookies": False,
}
</code></pre>



<p>Note: Normally you dont want to turn this off and normally you&#8217;d be generating HTML in a template like this: Please note the xsrf bit which adds the XSRF cookie.</p>



<pre class="wp-block-code"><code> &lt;form method="post" action="/register"&gt;
     &lt;input name="user_name" value="test@test.com"/&gt;
     &lt;input name="password" type="password"/&gt;
     &lt;input type="submit" value="submit"/&gt;
{% raw xsrf_form_html() %}
 &lt;/form&gt;
</code></pre>



<p>&#8212;EDIT following comments&#8212; Instead of:</p>



<pre class="wp-block-code"><code>  def get(self):
        loader = template.Loader("resources")
        page_contents = loader.load('register_page.html').generate()
        self.write(page_contents)
</code></pre>



<p>Do:</p>



<pre class="wp-block-code"><code>  def get(self):
     self.render("../resources/register_page.html")
</code></pre>



<p>or better:</p>



<pre class="wp-block-code"><code>  def get(self):
     self.render("register_page.html")
</code></pre>



<p>(and put it in your templates directory)</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2023/02/tornado-_xsrf-argument-missing-from-post/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>python tornado favicon 的設定方法</title>
		<link>https://stackoverflow.max-everyday.com/2017/09/python-tornado-favicon/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/09/python-tornado-favicon/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Sat, 30 Sep 2017 03:01:22 +0000</pubDate>
				<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=1431</guid>

					<description><![CDATA[官方的建議： http://www.tornad...]]></description>
										<content:encoded><![CDATA[<p>官方的建議：<br />
<a href="http://www.tornadoweb.org/en/stable/guide/running.html">http://www.tornadoweb.org/en/stable/guide/running.html</a></p>
<pre>user nginx;
worker_processes 1;

error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
    use epoll;
}

http {
    # Enumerate all the Tornado servers here
    upstream frontends {
        server 127.0.0.1:8000;
        server 127.0.0.1:8001;
        server 127.0.0.1:8002;
        server 127.0.0.1:8003;
    }

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    access_log /var/log/nginx/access.log;

    keepalive_timeout 65;
    proxy_read_timeout 200;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    gzip on;
    gzip_min_length 1000;
    gzip_proxied any;
    gzip_types text/plain text/html text/css text/xml
               application/x-javascript application/xml
               application/atom+xml text/javascript;

    # Only retry if there was a communication error, not a timeout
    # on the Tornado server (to avoid propagating "queries of death"
    # to all frontends)
    proxy_next_upstream error;

    server {
        listen 80;

        # Allow file uploads
        client_max_body_size 50M;

        location ^~ /static/ {
            root /var/www;
            if ($query_string) {
                expires max;
            }
        }
        location = /favicon.ico {
            rewrite (.*) /static/favicon.ico;
        }
        location = /robots.txt {
            rewrite (.*) /static/robots.txt;
        }

        location / {
            proxy_pass_header Server;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Scheme $scheme;
            proxy_pass http://frontends;
        }
    }
}</pre>
<hr />
<p>網友 1號建議：</p>
<p>You need to wrap favicon.ico with parenthesis and escape the period in the regular expression. Your code will become</p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">favicon_path </span><span class="pun">=</span> <span class="str">'/path/to/favicon.ico'</span><span class="pln">

settings </span><span class="pun">=</span> <span class="pun">{</span>
    <span class="str">'debug'</span><span class="pun">:</span> <span class="kwd">True</span><span class="pun">,</span> 
    <span class="str">'static_path'</span><span class="pun">:</span><span class="pln"> os</span><span class="pun">.</span><span class="pln">path</span><span class="pun">.</span><span class="pln">join</span><span class="pun">(</span><span class="pln">PATH</span><span class="pun">,</span> <span class="str">'static'</span><span class="pun">)}</span><span class="pln">

handlers </span><span class="pun">=</span> <span class="pun">[</span>
    <span class="pun">(</span><span class="pln">r</span><span class="str">'/'</span><span class="pun">,</span> <span class="typ">WebHandler</span><span class="pun">),</span>
    <span class="pun">(</span><span class="pln">r</span><span class="str">'/(favicon\.ico)'</span><span class="pun">,</span><span class="pln"> tornado</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="typ">StaticFileHandler</span><span class="pun">,</span> <span class="pun">{</span><span class="str">'path'</span><span class="pun">:</span><span class="pln"> favicon_path</span><span class="pun">})]</span><span class="pln">

application </span><span class="pun">=</span><span class="pln"> tornado</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="typ">Application</span><span class="pun">(</span><span class="pln">handlers</span><span class="pun">,</span> <span class="pun">**</span><span class="pln">settings</span><span class="pun">)</span><span class="pln">
application</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span class="pln">port</span><span class="pun">)</span><span class="pln">
tornado</span><span class="pun">.</span><span class="pln">ioloop</span><span class="pun">.</span><span class="typ">IOLoop</span><span class="pun">.</span><span class="pln">instance</span><span class="pun">().</span><span class="pln">start</span><span class="pun">()</span></code></pre>
<hr />
<p>網友2號建議：</p>
<p>You can simply do something like this:</p>
<p>a. delete static_path from the app settings.</p>
<p>b.</p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">handlers </span><span class="pun">=</span> <span class="pun">[</span>
            <span class="pun">(</span><span class="pln">r</span><span class="str">'/favicon.ico'</span><span class="pun">,</span><span class="pln"> tornado</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="typ">StaticFileHandler</span><span class="pun">,</span> <span class="pun">{</span><span class="str">'path'</span><span class="pun">:</span><span class="pln"> favicon_path</span><span class="pun">}),</span>
            <span class="pun">(</span><span class="pln">r</span><span class="str">'/static/(.*)'</span><span class="pun">,</span><span class="pln"> tornado</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="typ">StaticFileHandler</span><span class="pun">,</span> <span class="pun">{</span><span class="str">'path'</span><span class="pun">:</span><span class="pln"> static_path</span><span class="pun">}),</span>
            <span class="pun">(</span><span class="pln">r</span><span class="str">'/'</span><span class="pun">,</span> <span class="typ">WebHandler</span><span class="pun">)</span>
<span class="pun">]</span></code></pre>
<p>haven&#8217;t tested but I see no reason it shouldn&#8217;t work.</p>
<hr />
<p>官方是一定可以run, 但用起來有點怪怪的，因為當不使用 nginx 時就無法使用 /favicon.ico</p>
<p>我的解法：</p>
<p>(r&#8217;/(favicon\.ico)&#8217;, MyStaticFileHandler, {&#8216;path&#8217;: favicon_path}),</p>
<p>favicon_path 放的是 static 的這一個 folder, <strong>不是</strong>指到 /static/favicon.ico 但實際會存取 /static/favicon.ico 這樣真的很神奇。</p>
<hr />
<p>&nbsp;</p>
<p>如果是tornado处理全部请求的话的确要求写static_path，或者使用tornado.web.StaticHandler，而一般生产环境下部署的时候，我们一般会在前端部署一个nginx的，可以在这里做静态文件处理，把static目录下的文件让nginx来执行。</p>
<pre class="hljs nginx"><code><span class="hljs-section">server</span> 
{
        <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
        <span class="hljs-attribute">server_name</span> www.abc.com;
        <span class="hljs-attribute">index</span> index.html index.htm index.php;
        <span class="hljs-attribute">root</span>  /storage/www/abc;
        <span class="hljs-attribute">server_name_in_redirect</span>  <span class="hljs-literal">off</span>;
        <span class="hljs-attribute">access_log</span>  <span class="hljs-literal">off</span>;
        <span class="hljs-comment"># Allow file uploads</span>
        <span class="hljs-attribute">client_max_body_size</span> <span class="hljs-number">10M</span>;
        <span class="hljs-attribute">proxy_read_timeout</span> <span class="hljs-number">10</span>;

        <span class="hljs-attribute">location</span> = /favicon.ico {
            <span class="hljs-attribute">rewrite</span> (.*) /static/favicon.ico;
        }
        <span class="hljs-attribute">location</span> = /robots.txt {
            <span class="hljs-attribute">rewrite</span> (.*) /static/robots.txt;
        }

    <span class="hljs-attribute">location</span><span class="hljs-regexp"> ^~</span> /static/ {
                    <span class="hljs-attribute">root</span> /storage/www/abc;
                    <span class="hljs-attribute">if</span> (<span class="hljs-variable">$query_string</span>) {
                        <span class="hljs-attribute">expires</span> max;
                    }
                }

        <span class="hljs-attribute">location</span> / {
            <span class="hljs-attribute">proxy_pass_header</span> Server;
            <span class="hljs-attribute">proxy_set_header</span> Host <span class="hljs-variable">$http_host</span>;
            <span class="hljs-attribute">proxy_redirect</span> <span class="hljs-literal">off</span>;
            <span class="hljs-attribute">proxy_set_header</span> X-Real-IP <span class="hljs-variable">$remote_addr</span>;
            <span class="hljs-attribute">proxy_set_header</span> X-Scheme <span class="hljs-variable">$scheme</span>;
            <span class="hljs-attribute">proxy_pass</span> http://abc;
        }
}
</code></pre>
<p>当然，开发时为了方便，在tornado里配置一个static目录比较好。</p>
<hr />
<p>不知道為何，我的nginx 版本好像跟別人的不相容，上面的寫法都無法正確被套用到，我的設定值如下：</p>
<pre><code>
upstream myfrontends {
    server 127.0.0.1:5444;
}

server {
    listen       5443;
    server_name  my_server_name;
    charset utf-8;
    ssl on;
    ssl_certificate /etc/nginx/ssl/Server.pem;
    ssl_certificate_key /etc/nginx/ssl/Server.key;

    index index.html index.htm;
    root /my_server_path/static;
    server_name_in_redirect  off;
    access_log  off;

    location = /favicon.ico {
        rewrite (.*) /static/favicon.ico;
    }
    location = /robots.txt {
        rewrite (.*) /static/robots.txt;
    }

    location /static/
    {
        alias /my_server_path/static/;
    }
    location /css/
    {
        alias /my_server_path/static/css/;
    }
    location /js/
    {
        alias /my_server_path/static/js/;
    }
    location /images/
    {
        alias /my_server_path/static/images/;
    }

    location / {
        proxy_pass_header Server;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_pass http://myfrontends;
    }
}
</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/09/python-tornado-favicon/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>客製化 Tornado 的 StaticFile 的  404 not found page.</title>
		<link>https://stackoverflow.max-everyday.com/2017/09/tornado-staticfile-404-not-found-page/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/09/tornado-staticfile-404-not-found-page/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Tue, 19 Sep 2017 04:10:59 +0000</pubDate>
				<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=1360</guid>

					<description><![CDATA[滿簡單的，多包一層就可以處理掉。 The fil...]]></description>
										<content:encoded><![CDATA[<p>滿簡單的，多包一層就可以處理掉。</p>
<p>The file specified in <code>default_filename</code> should be in given static path. So if you move <code>error.html</code>to <code>assets/js</code> directory, than navigate to <code>/js/</code> you will see content of error.html.</p>
<p>Basically this functionality is a helper with limited usecase (imho). More at <a href="https://stackoverflow.com/a/27891339/681044">https://stackoverflow.com/a/27891339/681044</a>.</p>
<h2>Custom error pages</h2>
<p>Every request handler handles/renders errors in <a href="http://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write_error" rel="nofollow noreferrer"><code>write_error</code></a> function. This is the recommended way to create custom error pages:</p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="kwd">class</span> <span class="typ">MyStaticFileHandler</span><span class="pun">(</span><span class="pln">tornado</span><span class="pun">.</span><span class="pln">web</span><span class="pun">.</span><span class="typ">StaticFileHandler</span><span class="pun">):</span>

    <span class="kwd">def</span><span class="pln"> write_error</span><span class="pun">(</span><span class="pln">self</span><span class="pun">,</span><span class="pln"> status_code</span><span class="pun">,</span> <span class="pun">*</span><span class="pln">args</span><span class="pun">,</span> <span class="pun">**</span><span class="pln">kwargs</span><span class="pun">):</span>
        <span class="com"># custom 404 page</span>
        <span class="kwd">if</span><span class="pln"> status_code </span><span class="kwd">in</span> <span class="pun">[</span><span class="lit">404</span><span class="pun">]:</span><span class="pln">
            self</span><span class="pun">.</span><span class="pln">render</span><span class="pun">(</span><span class="str">'templates/error.html'</span><span class="pun">)</span>
        <span class="kwd">else</span><span class="pun">:</span><span class="pln">
            super</span><span class="pun">().</span><span class="pln">write_error</span><span class="pun">(</span><span class="pln">status_code</span><span class="pun">,</span> <span class="pun">*</span><span class="pln">args</span><span class="pun">,</span> <span class="pun">**</span><span class="pln">kwargs</span><span class="pun">)</span></code></pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/09/tornado-staticfile-404-not-found-page/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>tornado TLSV1_ALERT_UNKNOWN_CA</title>
		<link>https://stackoverflow.max-everyday.com/2017/09/tornado-tlsv1_alert_unknown_ca/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/09/tornado-tlsv1_alert_unknown_ca/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 06 Sep 2017 00:34:36 +0000</pubDate>
				<category><![CDATA[Java筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Tomcat]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=1230</guid>

					<description><![CDATA[我在自己的 NB 裡同時架了 2個 web se...]]></description>
										<content:encoded><![CDATA[<p>我在自己的 NB 裡同時架了 2個 web server, 使用 2個自己sign 的 ssl 憑證。使用行動裝置(iPad)先存取第1台 用 TimCat 架的 web server之後，再連到同一個 ip address 下的 tornado web server 會顯示錯誤訊息：</p>
<pre>[W 170906 08:19:15 iostream:1327] SSL Error on 9 ('192.168.1.55', 62014): [SSL: TLSV1_ALERT_UNKNOWN_CA] tlsv1 alert unknown ca (_ssl.c:661)</pre>
<p>而且是無窮迴圈， client side 會一直 retry&#8230;，大約每1秒可以呼叫到 8個 request。</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-1231" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2017/09/Screenshot-2017-09-06-08.30.08.jpg" alt="" width="640" height="375" srcset="https://stackoverflow.max-everyday.com/wp-content/uploads/2017/09/Screenshot-2017-09-06-08.30.08.jpg 640w, https://stackoverflow.max-everyday.com/wp-content/uploads/2017/09/Screenshot-2017-09-06-08.30.08-600x352.jpg 600w" sizes="(max-width: 640px) 100vw, 640px" /></p>
<p>解法當然就是 web server 分在不同台就好了&#8230;</p>
<hr />
<p>我是透過下面的指令來建立自有憑證：</p>
<p>I created SSL certificates using these steps:</p>
<p><strong>Create the CA private key:</strong></p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">openssl genrsa </span><span class="pun">-</span><span class="pln">des3 </span><span class="pun">-</span><span class="pln">out servercakey</span><span class="pun">.</span><span class="pln">pem </span></code></pre>
<p><strong>Create the CA public certificate</strong> <em>(When you create a certificate, there must be one unique name (a Distinguished Name (DN)), which is different for each certificate that you create):</em></p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">openssl req </span><span class="pun">-</span><span class="pln">new </span><span class="pun">-</span><span class="pln">x509 </span><span class="pun">-</span><span class="pln">key servercakey</span><span class="pun">.</span><span class="pln">pem </span><span class="pun">-</span><span class="pln">out root</span><span class="pun">.</span><span class="pln">crt </span></code></pre>
<p><strong>Create the server&#8217;s private key file:</strong></p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">openssl genrsa </span><span class="pun">-</span><span class="pln">out server</span><span class="pun">.</span><span class="pln">key </span></code></pre>
<p><strong>Create the server certificate request:</strong></p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">openssl req </span><span class="pun">-</span><span class="pln">new </span><span class="pun">-</span><span class="pln">out reqout</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">-</span><span class="pln">key server</span><span class="pun">.</span><span class="pln">key </span></code></pre>
<p><strong>Use the CA private key file to sign the server&#8217;s certificate:</strong></p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">openssl x509 </span><span class="pun">-</span><span class="pln">req </span><span class="pun">-</span><span class="kwd">in</span><span class="pln"> reqout</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">-</span><span class="pln">days </span><span class="lit">3650</span> <span class="pun">-</span><span class="pln">sha1 </span><span class="pun">-</span><span class="typ">CAcreateserial</span> <span class="pun">-</span><span class="pln">CA root</span><span class="pun">.</span><span class="pln">crt </span><span class="pun">-</span><span class="typ">CAkey</span><span class="pln"> servercakey</span><span class="pun">.</span><span class="pln">pem </span><span class="pun">-</span><span class="pln">out server</span><span class="pun">.</span><span class="pln">crt </span></code></pre>
<p><strong>Create the client&#8217;s private key file:</strong></p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">openssl genrsa </span><span class="pun">-</span><span class="pln">out client</span><span class="pun">.</span><span class="pln">key </span></code></pre>
<p><strong>Create the client certificate request:</strong></p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">openssl req </span><span class="pun">-</span><span class="pln">new </span><span class="pun">-</span><span class="pln">out reqout</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">-</span><span class="pln">key client</span><span class="pun">.</span><span class="pln">key </span></code></pre>
<p><strong>Use the CA private key file to sign the client&#8217;s certificate:</strong></p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">openssl x509 </span><span class="pun">-</span><span class="pln">req </span><span class="pun">-</span><span class="kwd">in</span><span class="pln"> reqout</span><span class="pun">.</span><span class="pln">txt </span><span class="pun">-</span><span class="pln">days </span><span class="lit">3650</span> <span class="pun">-</span><span class="pln">sha1 </span><span class="pun">-</span><span class="typ">CAcreateserial</span> <span class="pun">-</span><span class="pln">CA root</span><span class="pun">.</span><span class="pln">crt </span><span class="pun">-</span><span class="typ">CAkey</span><span class="pln"> servercakey</span><span class="pun">.</span><span class="pln">pem </span><span class="pun">-</span><span class="pln">out client</span><span class="pun">.</span><span class="pln">crt </span></code></pre>
<p><strong>Creating pem file for Server:</strong></p>
<pre class="lang-py prettyprint prettyprinted"><code><span class="pln">cat server</span><span class="pun">.</span><span class="pln">crt root</span><span class="pun">.</span><span class="pln">crt </span><span class="pun">&gt;</span><span class="pln"> server</span><span class="pun">.</span><span class="pln">pem</span></code></pre>
<p>上面很多指令是多餘的，產生的檔案也都沒有使用到，tornado 只使用到 2個：</p>
<pre> server_https = HTTPServer(self.app, xheaders=True, ssl_options = {
 "certfile": os.path.join(options.certificate_path, "<span style="color: #ff0000;"><strong>server.crt</strong></span>"),
 "keyfile": os.path.join(options.certificate_path, "<span style="color: #ff0000;"><strong>server.key</strong></span>"),
 })</pre>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/09/tornado-tlsv1_alert_unknown_ca/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Tornado static file</title>
		<link>https://stackoverflow.max-everyday.com/2017/09/tornado-static-file/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/09/tornado-static-file/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Mon, 04 Sep 2017 01:26:44 +0000</pubDate>
				<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=1193</guid>

					<description><![CDATA[Tornado 的靜態檔案，還滿好設定的，可以完...]]></description>
										<content:encoded><![CDATA[<p>Tornado 的靜態檔案，還滿好設定的，可以完全不動到 routes 裡的設定，只設<span class="s2">&#8220;static_path&#8221; 就可以了，如果要跳出 url 裡 /static/ 才需要去動到 routes 設定值</span>。Tornado 要效能高，似乎搭配 <a href="http://nginx.net/">nginx</a> 是主流。</p>
<p>&nbsp;</p>
<h2>Static files and aggressive file caching</h2>
<p>You can serve static files from Tornado by specifying the <code class="docutils literal"><span class="pre">static_path</span></code> setting in your application:</p>
<div class="highlight-default">
<div class="highlight">
<pre><span class="n">settings</span> <span class="o">=</span> <span class="p">{</span>
    <span class="s2">"static_path"</span><span class="p">:</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</span><span class="p">(</span><span class="vm">__file__</span><span class="p">),</span> <span class="s2">"static"</span><span class="p">),</span>
    <span class="s2">"cookie_secret"</span><span class="p">:</span> <span class="s2">"__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__"</span><span class="p">,</span>
    <span class="s2">"login_url"</span><span class="p">:</span> <span class="s2">"/login"</span><span class="p">,</span>
    <span class="s2">"xsrf_cookies"</span><span class="p">:</span> <span class="kc">True</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">application</span> <span class="o">=</span> <span class="n">tornado</span><span class="o">.</span><span class="n">web</span><span class="o">.</span><span class="n">Application</span><span class="p">([</span>
    <span class="p">(</span><span class="sa">r</span><span class="s2">"/"</span><span class="p">,</span> <span class="n">MainHandler</span><span class="p">),</span>
    <span class="p">(</span><span class="sa">r</span><span class="s2">"/login"</span><span class="p">,</span> <span class="n">LoginHandler</span><span class="p">),</span>
    <span class="p">(</span><span class="sa">r</span><span class="s2">"/(apple-touch-icon\.png)"</span><span class="p">,</span> <span class="n">tornado</span><span class="o">.</span><span class="n">web</span><span class="o">.</span><span class="n">StaticFileHandler</span><span class="p">,</span>
     <span class="nb">dict</span><span class="p">(</span><span class="n">path</span><span class="o">=</span><span class="n">settings</span><span class="p">[</span><span class="s1">'static_path'</span><span class="p">])),</span>
<span class="p">],</span> <span class="o">**</span><span class="n">settings</span><span class="p">)</span>
</pre>
</div>
</div>
<p>This setting will automatically make all requests that start with <code class="docutils literal"><span class="pre">/static/</span></code> serve from that static directory, e.g., <code class="docutils literal"><span class="pre">http://localhost:8888/static/foo.png</span></code> will serve the file <code class="docutils literal"><span class="pre">foo.png</span></code> from the specified static directory. We also automatically serve <code class="docutils literal"><span class="pre">/robots.txt</span></code> and <code class="docutils literal"><span class="pre">/favicon.ico</span></code> from the static directory (even though they don’t start with the <code class="docutils literal"><span class="pre">/static/</span></code> prefix).</p>
<p>from:<br />
<a href="http://www.tornadoweb.org/en/stable/guide/running.html">http://www.tornadoweb.org/en/stable/guide/running.html</a></p>
<hr />
<p>實際上 <span class="pl-k">class</span> <span class="pl-en">StaticFileHandler</span>(<span class="pl-e">RequestHandler</span>): 的 source code 及用法：</p>
<p><a href="https://github.com/tornadoweb/tornado/blob/master/tornado/web.py">https://github.com/tornadoweb/tornado/blob/master/tornado/web.py</a></p>
<p>&nbsp;</p>
<hr />
<p>如果沒有搭配nginx 處理 favicon.ico 的解法：</p>
<p>python tornado favicon 的設定方法<br />
<a href="https://stackoverflow.max-everyday.com/2017/09/python-tornado-favicon/">https://stackoverflow.max-everyday.com/2017/09/python-tornado-favicon/</a></p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/09/tornado-static-file/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Python tornado multi task</title>
		<link>https://stackoverflow.max-everyday.com/2017/03/python-tornado-multi-task/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/03/python-tornado-multi-task/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Thu, 16 Mar 2017 13:37:54 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Dropboxlike]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=370</guid>

					<description><![CDATA[透過 IOLoop 的add_callback ...]]></description>
										<content:encoded><![CDATA[<p>透過 IOLoop 的add_callback + self.finish() 可以讓 tornado 繼續在背景工作，但有一個問題是前一個工作太忙，會造成整個伺服器無法接受新的 request.</p>
<p>這要寫的有效率，似乎滿難的。</p>
<p>發現把某一個 function 加入 @gen.coroutine，主程式遇到這一個 function 自動變成 muti-thread .. @_@;</p>
<p>&nbsp;</p>
<h4>相關文章：</h4>
<p>Tornado &#8211; getting return value from IOLoop.add_callback()<br />
<a href="http://stackoverflow.com/questions/33742023/tornado-getting-return-value-from-ioloop-add-callback">http://stackoverflow.com/questions/33742023/tornado-getting-return-value-from-ioloop-add-callback</a></p>
<p>I&#8217;m not sure what the difference is between the following two functions:</p>
<pre><code> IOLoop.add_callback(callback, *args, **kwargs) 
</code></pre>
<p>AND</p>
<pre><code>IOLoop.spawn_callback(callback, *args, **kwargs)</code></pre>
<hr />
<p>You can&#8217;t get a return value, at least not in the way you&#8217;re thinking of. Your callback can&#8217;t even run until the function that called <code>spawn_callback</code> (i.e. <code>on_message</code>) returns. Just do whatever you want to do when that callback finishes in the callback itself (or pass it another callback to invoke with its result).</p>
<p><code>stack_context</code> is an error-handling mechanism for callback-oriented code. It doesn&#8217;t let you handle exceptions in the calling function, but it does let you establish an error handler in the calling function that will &#8220;follow&#8221; the code even through a chain of multiple <code>add_callback</code> calls. This turned out to be confusing as often as it helped, so now that coroutines are available I recommend using coroutines as much as possible and when you do need to fall back to raw callbacks, use <code>spawn_callback</code> instead of <code>add_callback</code> and put your try/except handler in the callback itself.</p>
<hr />
<p><a href="https://huangxiaohen2738.gitbooks.io/tornado-tcp-program/content/chapter2-2.html">https://huangxiaohen2738.gitbooks.io/tornado-tcp-program/content/chapter2-2.html</a></p>
<h2 id="22-ioloop的回调函数">2.2 ioloop的回调函数</h2>
<h5 id="1ioloopaddcallbackcallback-args-kwargs">1.IOLoop.add_callback(callback, <em>args, *</em>kwargs)</h5>
<p class="comments-section">这个函数是最简单的，在ioloop开启后执行的回调函数callback，<em>args和*</em>kwargs都是这个回调函数的参数。一般我们的server都是单进程单线程的，即使是多线程，那么这个函数也是安全的。</p>
<h5 id="8ioloopspawncallbackcallback-args-kwargs">8.IOLoop.spawn_callback(callback, <em>args, *</em>kwargs)</h5>
<p class="comments-section">这个函数也是去执行一个回调函数，但是和上面说过的其他callback不同，它和回调者的栈上下文没有关联，因此呢，他比较时候去做一些独立的功能回调。</p>
<hr />
<p>Tornado Execution Order Spawn callback<br />
<a href="http://stackoverflow.com/questions/38696131/tornado-execution-order-spawn-callback">http://stackoverflow.com/questions/38696131/tornado-execution-order-spawn-callback</a></p>
<p>No, in fact you&#8217;re guaranteed that all the functions are spawned before any of them starts running, because <code>first</code> does not <code>yield</code> between spawning <code>func</code> and spawning <code>func2</code>. You can verify this yourself by testing your code:</p>
<pre><code>from tornado import gen, ioloop

@gen.coroutine
def func():
    print('func started')
    yield gen.moment
    print('func done')


@gen.coroutine
def func2():
    print('func2 started')
    yield gen.moment
    print('func2 done')


@gen.coroutine
def first():
    for i in range(2):
        ioloop.IOLoop.current().spawn_callback(func)

    ioloop.IOLoop.current().spawn_callback(func2)
    yield gen.sleep(1)

ioloop.IOLoop.current().run_sync(first)
</code></pre>
<p>It prints:</p>
<pre><code>func started
func started
func2 started
func done
func done
func2 done
</code></pre>
<p>See, <code>func2</code> begins before the coroutines running <code>func</code> complete.</p>
<p>To accomplish what you want:</p>
<pre><code>@gen.coroutine
def first():
    yield [func() for i in range(2)]
    ioloop.IOLoop.current().spawn_callback(func2)
</code></pre>
<p>This prints:</p>
<pre><code>func started
func started
func done
func done
func2 started
func2 done
</code></pre>
<p>If you want <code>first</code> to wait for <code>func2</code> to finish before it exits, then:</p>
<pre><code>@gen.coroutine
def first():
    yield [func() for i in range(2)]
    yield func2()
</code></pre>
<p>For more info on calling coroutines from coroutines, see my <a href="https://emptysqua.re/blog/refactoring-tornado-coroutines/" rel="nofollow">Refactoring Tornado Coroutines</a>.</p>
<hr />
<p>官方文件：</p>
<p><a href="http://www.tornadoweb.org/en/stable/ioloop.html">http://www.tornadoweb.org/en/stable/ioloop.html</a></p>
<p>&nbsp;</p>
<h3>Running in the background</h3>
<p><a class="reference internal" title="tornado.ioloop.PeriodicCallback" href="http://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.PeriodicCallback"><code class="xref py py-obj docutils literal"><span class="pre">PeriodicCallback</span></code></a> is not normally used with coroutines. Instead, a coroutine can contain a <code class="docutils literal"><span class="pre">while</span> <span class="pre">True:</span></code> loop and use <a class="reference internal" title="tornado.gen.sleep" href="http://www.tornadoweb.org/en/stable/gen.html#tornado.gen.sleep"><code class="xref py py-obj docutils literal"><span class="pre">tornado.gen.sleep</span></code></a>:</p>
<div class="highlight-python">
<div class="highlight">
<pre><span class="nd">@gen.coroutine</span>
<span class="k">def</span> <span class="nf">minute_loop</span><span class="p">():</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="k">yield</span> <span class="n">do_something</span><span class="p">()</span>
        <span class="k">yield</span> <span class="n">gen</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>

<span class="c1"># Coroutines that loop forever are generally started with</span>
<span class="c1"># spawn_callback().</span>
<span class="n">IOLoop</span><span class="o">.</span><span class="n">current</span><span class="p">()</span><span class="o">.</span><span class="n">spawn_callback</span><span class="p">(</span><span class="n">minute_loop</span><span class="p">)</span>
</pre>
</div>
</div>
<p>Sometimes a more complicated loop may be desirable. For example, the previous loop runs every <code class="docutils literal"><span class="pre">60+N</span></code> seconds, where <code class="docutils literal"><span class="pre">N</span></code> is the running time of <code class="docutils literal"><span class="pre">do_something()</span></code>. To run exactly every 60 seconds, use the interleaving pattern from above:</p>
<div class="highlight-python">
<div class="highlight">
<pre><span class="nd">@gen.coroutine</span>
<span class="k">def</span> <span class="nf">minute_loop2</span><span class="p">():</span>
    <span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
        <span class="n">nxt</span> <span class="o">=</span> <span class="n">gen</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span>   <span class="c1"># Start the clock.</span>
        <span class="k">yield</span> <span class="n">do_something</span><span class="p">()</span>  <span class="c1"># Run while the clock is ticking.</span>
        <span class="k">yield</span> <span class="n">nxt</span>             <span class="c1"># Wait for the timer to run out.</span></pre>
</div>
</div>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/03/python-tornado-multi-task/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>非同步處理縮圖</title>
		<link>https://stackoverflow.max-everyday.com/2017/02/async-thumbnail/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/02/async-thumbnail/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 15 Feb 2017 18:47:06 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Dropboxlike]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=151</guid>

					<description><![CDATA[使用者上傳了檔案到伺服器上，理論上把資料放進資料...]]></description>
										<content:encoded><![CDATA[<p>使用者上傳了檔案到伺服器上，理論上把資料放進資料庫應該就可以閃人了，不需要同步等伺服器產生完縮圖。</p>
<p>解法是使用非同步的寫法在tornado web server 上。伺服器的執行畫面：</p>
<p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-152" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2017/02/Screenshot-2017-02-16-02.36.36.jpg" alt="" width="800" height="356" /></p>
<p>說明， 在呼叫了 /1/files/upload 只花了 10.27ms 就結束和使用者的連線。在此修改之前，同步等縮圖處理完需要 400~500ms 才能結束連線。</p>
<p>修改前的執行結果：<br />
<img loading="lazy" decoding="async" class="alignnone size-full wp-image-153" src="https://stackoverflow.max-everyday.com/wp-content/uploads/2017/02/Screenshot-2017-02-16-02.20.10.jpg" alt="" width="800" height="96" /></p>
<p>&nbsp;</p>
<h4>相關文章：</h4>
<blockquote class="wp-embedded-content" data-secret="BcO99mnMj8"><p><a href="https://stackoverflow.max-everyday.com/2017/02/tornado-coroutine-asynchronous/">認識 tornado 的 coroutine 和  asynchronous</a></p></blockquote>
<p><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="認識 tornado 的 coroutine 和  asynchronous &#8212; Max的程式語言筆記" src="https://stackoverflow.max-everyday.com/2017/02/tornado-coroutine-asynchronous/embed/#?secret=IfcbjEcUXL#?secret=BcO99mnMj8" data-secret="BcO99mnMj8" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe></p>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/02/async-thumbnail/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>認識 tornado 的 coroutine 和  asynchronous</title>
		<link>https://stackoverflow.max-everyday.com/2017/02/tornado-coroutine-asynchronous/</link>
					<comments>https://stackoverflow.max-everyday.com/2017/02/tornado-coroutine-asynchronous/#respond</comments>
		
		<dc:creator><![CDATA[max-stackoverflow]]></dc:creator>
		<pubDate>Wed, 15 Feb 2017 17:45:21 +0000</pubDate>
				<category><![CDATA[Dropboxlike開發筆記]]></category>
		<category><![CDATA[Python筆記]]></category>
		<category><![CDATA[Dropboxlike]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>
		<guid isPermaLink="false">http://stackoverflow.max-everyday.com/?p=144</guid>

					<description><![CDATA[Coroutine 可以讓我們在程式中按照自己的...]]></description>
										<content:encoded><![CDATA[<p>Coroutine 可以讓我們在程式中按照自己的意思去安排執行順序，允許短暫離開 function 並且保留 local variable 的狀態，等到某個時間點再跳回來，從上一次離開的地方繼續。</p>
<p>在 single thread 下，你執行到一個 blocking function，這時候如果讓 CPU 去做其他事情是不是很好，等到 I/O 有回應的，再跳回來原本的地方繼續執行。前提是執行在 single thread.</p>
<p>官方的說明文件：<br />
<a href="http://www.tornadoweb.org/en/stable/guide/coroutines.html">http://www.tornadoweb.org/en/stable/guide/coroutines.html</a></p>
<p>或</p>
<p><a href="http://www.tornadoweb.org/en/stable/guide/async.html">http://www.tornadoweb.org/en/stable/guide/async.html</a></p>
<p>或</p>
<p><a href="http://www.tornadoweb.org/en/stable/gen.html">http://www.tornadoweb.org/en/stable/gen.html</a></p>
<p>&nbsp;</p>
<p>用 tornado 做网站 (7)<br />
<a href="http://wiki.jikexueyuan.com/project/start-learning-python/309.html">http://wiki.jikexueyuan.com/project/start-learning-python/309.html</a></p>
<p>Blocking tasks in Tornado<br />
<a href="https://lbolla.info/blog/2013/01/22/blocking-tornado">https://lbolla.info/blog/2013/01/22/blocking-tornado</a></p>
<p>使用tornado让你的请求异步非阻塞<br />
<a href="http://www.dongwm.com/archives/shi-yong-tornadorang-ni-de-qing-qiu-yi-bu-fei-zu-sai/">http://www.dongwm.com/archives/shi-yong-tornadorang-ni-de-qing-qiu-yi-bu-fei-zu-sai/</a></p>
<hr />
<p>Here is a sample synchronous function:</p>
<div class="highlight-python">
<div class="highlight">
<pre><span class="kn">from</span> <span class="nn">tornado.httpclient</span> <span class="kn">import</span> <span class="n">HTTPClient</span>

<span class="k">def</span> <span class="nf">synchronous_fetch</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
    <span class="n">http_client</span> <span class="o">=</span> <span class="n">HTTPClient</span><span class="p">()</span>
    <span class="n">response</span> <span class="o">=</span> <span class="n">http_client</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">response</span><span class="o">.</span><span class="n">body</span></pre>
</div>
</div>
<p>&nbsp;</p>
<p>修改成非同步：</p>
<p>And here is the same function rewritten to be asynchronous with a callback argument:</p>
<div class="highlight-python">
<div class="highlight">
<pre><span class="kn">from</span> <span class="nn">tornado.httpclient</span> <span class="kn">import</span> <span class="n">AsyncHTTPClient</span>

<span class="k">def</span> <span class="nf">asynchronous_fetch</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">callback</span><span class="p">):</span>
    <span class="n">http_client</span> <span class="o">=</span> <span class="n">AsyncHTTPClient</span><span class="p">()</span>
    <span class="k">def</span> <span class="nf">handle_response</span><span class="p">(</span><span class="n">response</span><span class="p">):</span>
        <span class="n">callback</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
    <span class="n">http_client</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">callback</span><span class="o">=</span><span class="n">handle_response</span><span class="p">)</span></pre>
</div>
</div>
<p>&nbsp;</p>
<p>再修改成 coroutine:</p>
<p>Here is the coroutine version of our sample function, which is very similar to the original synchronous version:</p>
<div class="highlight-python">
<div class="highlight">
<pre><span class="kn">from</span> <span class="nn">tornado</span> <span class="kn">import</span> <span class="n">gen</span>

<span class="nd">@gen.coroutine</span>
<span class="k">def</span> <span class="nf">fetch_coroutine</span><span class="p">(</span><span class="n">url</span><span class="p">):</span>
    <span class="n">http_client</span> <span class="o">=</span> <span class="n">AsyncHTTPClient</span><span class="p">()</span>
    <span class="n">response</span> <span class="o">=</span> <span class="k">yield</span> <span class="n">http_client</span><span class="o">.</span><span class="n">fetch</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
    <span class="k">raise</span> <span class="n">gen</span><span class="o">.</span><span class="n">Return</span><span class="p">(</span><span class="n">response</span><span class="o">.</span><span class="n">body</span><span class="p">)</span>
</pre>
</div>
</div>
<p>The statement <code class="docutils literal"><span class="pre">raise</span> <span class="pre">gen.Return(response.body)</span></code> is an artifact of Python 2, in which generators aren’t allowed to return values. To overcome this, Tornado coroutines raise a special kind of exception called a <a class="reference internal" title="tornado.gen.Return" href="http://www.tornadoweb.org/en/stable/gen.html#tornado.gen.Return"><code class="xref py py-obj docutils literal"><span class="pre">Return</span></code></a>. The coroutine catches this exception and treats it like a returned value. In Python 3.3 and later, a <code class="docutils literal"><span class="pre">return</span> <span class="pre">response.body</span></code> achieves the same result</p>
<p>&nbsp;</p>
<hr />
<p>asynchronous 範例，發出一個 HTTP request 去抓 Yahoo weather 的資訊，然後利用 XML parser 從回應得資料中取出溫度：</p>
<pre>from tornado.ioloop import IOLoop
from tornado.web import Application, RequestHandler, asynchronous
from tornado.httpclient import AsyncHTTPClient
from xml.dom import minidom
 
class MainHandler(RequestHandler):
    url = "http://weather.yahooapis.com/forecastrss?w=2306179&amp;u=c"
 
    @asynchronous
    def get(self):
        http_client = AsyncHTTPClient()
        http_client.fetch(self.url, callback=self._on_fetch)
 
    def _on_fetch(self, response):
        degree = self._parse_xml(response.body)
        self.finish("Taipei: %d" % degree)
 
    def _parse_xml(self, xml):
        xml_doc = minidom.parseString(xml)
        weather_list = xml_doc.getElementsByTagName('yweather:condition')
        degree = float(weather_list[0].attributes['temp'].value)
        return degree
 
if __name__ == "__main__":
    application = Application([
        (r"/", MainHandler),
    ])
    application.listen(8888)
    IOLoop.instance().start()
</pre>
<hr />
<p>換成 coroutine:</p>
<pre>from tornado.ioloop import IOLoop
from tornado.web import Application, RequestHandler, asynchronous
from tornado.httpclient import AsyncHTTPClient
import tornado.gen as gen
from xml.dom import minidom
 
class MainHandler(RequestHandler):
    url = "http://weather.yahooapis.com/forecastrss?w=2306179&amp;u=c"
 
    @gen.coroutine
    def get(self):
        http_client = AsyncHTTPClient()
        response = yield http_client.fetch(self.url)
        degree = self._parse_xml(response.body)
        self.finish("Taipei: %d" % degree)
 
    def _parse_xml(self, xml):
        xml_doc = minidom.parseString(xml)
        weather_list = xml_doc.getElementsByTagName('yweather:condition')
        degree = float(weather_list[0].attributes['temp'].value)
        return degree
 
if __name__ == "__main__":
    application = Application([
        (r"/", MainHandler),
    ])
    application.listen(8888)
    IOLoop.instance().start()
</pre>
<hr />
<p>asynchronous + callback 範例：</p>
<pre>class MainHandler(RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        req1(argument1, callback=self._res1)
 
    @tornado.web.asynchronous
    def _res1(self, response1):
        ...do something with response
        req2(argument2, callback=self._res2)
 
    def _res2(self, response2):
        ...do something with response
        self.finish("result...")
</pre>
<hr />
<p>換成 coroutine</p>
<p>&nbsp;</p>
<pre>class MainHandler(RequestHandler):    
    @tornado.gen.coroutine
    def get(self):
        response1 = yield req1(argument1)
        ...do something with response1
        response2 = yield req2(argument2)
        ...do something with response2
        self.finish("result...")
</pre>
<hr />
<p>資料來源：<br />
Coroutine in Tornado Web Framework<br />
<a href="http://blog.yslin.tw/2014/04/coroutine-in-tornado-web-framework.html">http://blog.yslin.tw/2014/04/coroutine-in-tornado-web-framework.html</a></p>
<hr />
<p>sample:<br />
<a href="http://stackoverflow.com/questions/8812715/using-a-simple-python-generator-as-a-co-routine-in-a-tornado-async-handler?rq=1">http://stackoverflow.com/questions/8812715/using-a-simple-python-generator-as-a-co-routine-in-a-tornado-async-handler?rq=1</a></p>
<p>Here&#8217;s a basic version of what you are describing. To avoid blocking you can pass your generator to the IOLoop via a callback function. The trick here is since you are not using a process that does actual IO and so has no os level process/file handler to add to the IOLoop via add_handler, you can instead use a simple add_callback call and call it repeatedly from within the callback function to keep the function in the IOLoop callback queue until the generator has finished.</p>
<pre>
import tornado.httpserver
import tornado.ioloop
import tornado.web

class TextHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        self.generator = self.generate_text(1000)
        tornado.ioloop.IOLoop.instance().add_callback(self.loop)

    def loop(self):
        try:
            text = self.generator.next()
            self.write(text)
            tornado.ioloop.IOLoop.instance().add_callback(self.loop)
        except StopIteration:
            self.finish()

    def generate_text(self, n):
        for x in xrange(n):
            if not x % 15:
                yield "FizzBuzz\n"
            elif not x % 5:
                yield "Buzz\n"
            elif not x % 3:
                yield "Fizz\n"
            else:
                yield "%s\n" % x

application = tornado.web.Application([
    (r"/text/", TextHandler),
])

http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
tornado.ioloop.IOLoop.instance().start()
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://stackoverflow.max-everyday.com/2017/02/tornado-coroutine-asynchronous/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
