<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[小本本]]></title><description><![CDATA[✏️📖🖥️]]></description><link>https://xcel.me/</link><image><url>https://xcel.me/favicon.png</url><title>小本本</title><link>https://xcel.me/</link></image><generator>Ghost 4.32</generator><lastBuildDate>Mon, 30 Mar 2026 06:10:28 GMT</lastBuildDate><atom:link href="https://xcel.me/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Love and Hate to Rust -- Two Years' Journey of a Data Engineer]]></title><description><![CDATA[I want to share my first hand knowledge, using my 5 years' experience in data engineering, to tell you the great things and the bad things Rust has to offer in this field.]]></description><link>https://xcel.me/love-and-hate-to-rust-two-years-journey-of-a-data-engineer/</link><guid isPermaLink="false">6724f21c8b9861105f980245</guid><category><![CDATA[programming]]></category><category><![CDATA[Rust]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Fri, 01 Nov 2024 15:40:11 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1484715340216-6e77040b1852?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIwfHxjcmFifGVufDB8fHx8MTczMDQ3NTE0OHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1484715340216-6e77040b1852?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDIwfHxjcmFifGVufDB8fHx8MTczMDQ3NTE0OHww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="Love and Hate to Rust -- Two Years&apos; Journey of a Data Engineer"><p>To be clear, I&apos;m not evangelist of the language XD, some folks may have strong opinions against it. This article is just purely based on my 2 years&apos; usage of the Rust programming language and its ecosystem. And I want to share my first hand knowledge, using my 5 years&apos; experience in data engineering, to tell you the great things and the bad things Rust has to offer in this field.</p>
<h3 id="1-start-with-python-%F0%9F%90%8D">1. Start with Python &#x1F40D;</h3>
<p>About 5 years ago, when I was tired of being a python script kiddie, I was very lucky to be hired by a startup data company, where I learned everything about this industry -- batching, streaming, lakehouse, warehouse, extraction, transformation, ingestion... The company has different clients, big and small. Some has to deal with billions of records in their DBMS, some needs to do complex analytical computation on their private servers. And I was on the team responsible for our ETL toolkits, which we developed and integrated as a user friendly low-code platform. It was built around Pandas and Apache Spark, Pandas is for exploration and previewing, PySpark for batch processing, a very common data handling architecture. So naturally for me, I started working on it very quickly with my Monty Python knowledge.</p>
<p>Not so long after, however, we had some hard time with this architecture. I could spin up a chart/sheet very quickly using Pandas, but when the sample dataset became larger than usual sometimes, the OOM killer kicked in very timely. Classic pandas experience, and we had to increase the memory limit temporarily for it. Sometimes the data is too skewed, making one spark worker node crash repeatedly, dragging the whole spark cluster into a death loop. So we had to redo the partitioning, and the process is kinda slow and painful. You have to introduce some trial and error when the dataset is too large, doing any reasonable insights on it is very time and resource consuming. The story goes on and on...</p>
<h3 id="2-meet-rust-%F0%9F%A6%80">2. Meet Rust &#x1F980;</h3>
<p>So, Pandas is smart, but it eats up exponential memory with bigger datasets. Spark can do the heavy lifting, but it&apos;s slow and not so flexible. Is there any tool smart/flexible enough, not so resource hungry, and can even do some huge data processing? We quickly eyed on the then newly born <a href="https://pola.rs/">Polars</a> project, which is mainly developed in Rust. It offers a similar experience with Pandas, and also provides a python package, so the most common things you do in Pandas can be done in Polars, just faster and using less memory, which is very convenient. However, Polars was not very feature complete then, many complex transformations was missing from it and it was not very extensive to develop with easily. And it does not feature a complete SQL engine, which was also needed by us.</p>
<p>Here comes <a href="https://datafusion.apache.org/">Apache DataFusion</a> then. It boasts a truly extensive query engine, also written in Rust, provides equal or better performance compared to Polars, supports hugely flexible data sources and data transformations. So we quickly did some experiments with it and was very satisfied. At that time, not many DBMSs was officially supported by it, we shamelessly dig from the Polars dependencies and found the <a href="https://github.com/sfu-db/connector-x">connector-x</a> crate, which is a high performance Rust connector to all kinds of DBMSs (BTW I made several PRs and were merged to it afterwards), and with several adapter traits and structs, we were able to connect DataFusion to all these MySQL, Postgres, Oracle, SQL Server... you name it, which was mind boggling and insanely productive. The Rust ecosystem was that vibrant and abundant and it&apos;s still thriving!</p>
<h3 id="3-the-good-time">3. The Good Time</h3>
<p>After several testing deployments replacing Spark, we were very satisfied with our DataFusion based query engine. It generally outperformed Spark not only in resource consumption, but also in speed. Even with much smaller datasets, it still managed to beat Spark due to node scheduling overhead in Spark, making realtime query execution possible. Clients and on-site engineers were also kinda surprised by this large improvement. So we decided to also publish it as an additional OLAP service into out ETL toolkits platform. Then things went even more smoothly (or rusty? lol). Thanks to the highly extensive nature of DataFusion and the fantastic tooling ecosystem of Rust, we managed to develop several SDK packages in mere weeks for our newly developed query engine, supporting Python and Go, making our other existing services blew their old versions out of the water as well, which blew our minds once again.</p>
<p>My first year with Rust went fly without me even realizing it. We had been busy constantly improving our query engine, building more supporting tools, online services, adding supports for more DBs, files and storage systems, doing all kinds of customizations for clients, and so on. Two full-time golang developers in our team were also converted to Rust (Rust is not cult, I promise). Overall, the Rust ecosystem is amazing, it&apos;s lively and rapidly evolving. You can find packages for all kinds of common functionalities on the public <a href="https://crates.io">crates.io</a> repository. There is an official dedicated <a href="https://docs.rs">docs.rs</a> site for documents. The Rust book and the Rust async book are extremely helpful even to complete newbie programmers. Also, don&apos;t forget <a href="https://doc.rust-lang.org/rust-by-example/index.html">rust-by-example</a>, the <a href="https://play.rust-lang.org/">rust playground</a>, and a very nice <a href="https://tourofrust.com">tour of rust</a> if you are impressed by the tour of golang, and so on. There are countless Rust tutorials on the Internet, different types of learners can choose the best ones for themselves. Moreover, there&apos;re several online forums if you need extra help, like the <a href="https://www.reddit.com/r/rust/">r/rust</a> subreddit, or the official rust <a href="https://users.rust-lang.org/">users&apos; forum</a>. Many have the impression that Rust communities are toxic, hostile, or hard to work with. I can&apos;t say for everyone, but on our team, we generally find people in Rust communities are very eager to help. There is no &quot;deep&quot; knowledge kept away from new comers, hardly any &quot;RTFM&quot; humiliating comments which are quite common on the Stack Overflow sites, and I think are rude and unproductive. And to be fair, I personally find /r/rust much more helpful than SO, on /r/rust people can rant about all sorts of things Rust, help requests, advice seeks, new creation showoffs...serious or casual, they are not restricted to the rigid Question then Answer paradigm, people just exchange their knowledge freely, no superior or inferior. Though they can&apos;t avoid some issues from reddit&apos;s side, like information is hard to archive and search, formatting is messy, etc., but I think it&apos;s good enough for a free community for the time being.</p>
<h3 id="4-the-bad-time">4. The Bad Time</h3>
<p>About another year went by, our team landed more clients than ever. But some little things we thought irrelevant went messy fast. First, we found the CI became unbearably slow while test builds on multiple branches were waiting in queue. It turned out to be a caching problem of the cargo building architecture, combined with the complex CI environment, there were no sound solution to this. We had to seek to dark magic to mitigate (I also wrote <a href="https://xcel.me/gitlab-ci-zhong-gei-cargo-jia-su-bian-yi/">a post about this</a> in Chinese). One day our devops colleague came up and ask what we were doing on the CI machine, our jobs consumed most of the CPU time, complaints from other teams were made from time to time, because their jobs kept crashing everyday. Yeah, you probably already knew it, cargo build is quite CPU intensive, and by default it eats up all the cores. We had to set limits to the builder containers, but the building time dragged long and long again. To reduce the wait for any changes to be landed, we even tried to broke our monolithic project into a modular, microservice design gradually, but quickly found out that the maintenance cost is punishing. You may ask, why not throw more machines to it? Truth is, we were still a startup, more machines calls for more maintenance efforts. And the CI cluster we had was by no means weak, around 256 cores, 1TB of ram in total, and a JBOD array god knows how many terabytes of disks it packed within. We were desperate to try to find ways to cut down the wait for compiling.</p>
<h3 id="5-how-we-improved">5. How We Improved</h3>
<p>Here are some strategies we tried and had applied to the dependencies in <code>cargo.toml</code>. The core idea of it is being very careful about which crate and which version to pull in, in order to shrink the dependency tree as best as we can. <strong>a)</strong> Remove any unnecessary, or <a href="https://en.wikipedia.org/wiki/Npm_left-pad_incident">&quot;left-pad&quot;</a> like dependency, re-implement in our own code base if possible. And now we know that the cargo build directory is just as heavy as node_modules.&#x1F605; <strong>b)</strong> Hand pick versions of the dependencies. Usually, there are overlapping among dependencies, dependencies of dependencies and so on, but just with different versions. Choose wisely on crates with a huge dependency tree, so that the result dependency tree could be as low and narrow as possible. <strong>c)</strong> If method b) doesn&apos;t work very well for some particular crate, mirror it on our own git server, re-export and/or modify some of its dependency. Sometimes this method might need us mirroring several other crates in a row, so we only use this if absolutely necessary.</p>
<p>Generally, these strategies helped us cut down about 50% of the dependency tree, and test build pipeline took us around 30 min with a limit of 16 CPU cores at that time. But it was still to wasteful compared to our non CGO golang projects, which generally take only about one or two minutes. And we had to find other ways. One thing we first thought about was the feature flag. Most of the time when we did the test builds, the relavant code paths were only limited to several specific packages within the code base. After we gated several lesser used packages behind feature flags, the time for one test build came down to around 20 mins. That was a nice but not that significant improvement.</p>
<p>Another thing we changed is the cargo build profile, see <a href="https://doc.rust-lang.org/cargo/reference/profiles.html">the Cargo Document</a> on this topic. We were using release profile for the test builds before, because some test routes on non optimized debug build profile were too slow. We played with several options then decided to tune down the <code>opt-level</code> and <code>lto</code> options, along with the usage of <a href="https://github.com/rui314/mold"><code>mold</code> the multithreaded modern linker</a>, the build time was brought down to about 10 mins or less.</p>
<h3 id="6-to-be-continued">6. To be Continued</h3>
<p>At this point, 10 mins for us is tolerable for now. But the rabbithole is way more deep, many more aspects we could still improve upon. And there are more rusty things I wanna share with you. More posts coming in...</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Go 调用 Rust 动态库：FFI 零拷贝传递 Arrow 数据块]]></title><description><![CDATA[Go 与 Rust 间的 FFI 调用踩坑学习]]></description><link>https://xcel.me/go-rust-ffi/</link><guid isPermaLink="false">64cfcc91b9aecb71c91c2311</guid><category><![CDATA[Rust]]></category><category><![CDATA[Go]]></category><category><![CDATA[FFI]]></category><category><![CDATA[programming]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Sun, 06 Aug 2023 18:21:41 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>&#x6700;&#x8FD1;&#x5C0F;&#x672C;&#x672C;&#x535A;&#x4E3B;&#x6211;&#x63A5;&#x5230;&#x9879;&#x9887;&#x5177;&#x6311;&#x6218;&#x6027;&#x7684;&#x5DE5;&#x4F5C;&#xFF0C;&#x5177;&#x4F53;&#x662F;&#x8981;&#x628A;&#x539F;&#x6765;&#x4E00;&#x4E2A;&#x4E1A;&#x52A1;&#x4E2D;&#x7ACB;&#x7684; Rust &#x6A21;&#x5757;&#x5C01;&#x88C5;&#x4E3A;&#x52A8;&#x6001;&#x5E93;&#x63D0;&#x4F9B;&#x7ED9;&#x5176;&#x4ED6;&#x56E2;&#x961F;&#x4F7F;&#x7528;&#xFF0C;&#x9700;&#x6C42;&#x662F;&#x80FD;&#x901A;&#x8FC7; Arrow &#x683C;&#x5F0F;&#x4EA4;&#x6362;&#x6570;&#x636E;&#xFF0C;&#x540C;&#x65F6;&#x8FD8;&#x8981;&#x63D0;&#x4F9B; ETL &#x4E2D;&#x5E38;&#x89C1;&#x7684;&#x4E00;&#x4E9B;  T(ransformation) &#x64CD;&#x4F5C;&#x3002;&#x76EE;&#x524D;&#x6709; Go &#x670D;&#x52A1;&#x9700;&#x8981;&#x63A5;&#x5165;&#xFF0C;&#x6240;&#x4EE5;&#x8FD8;&#x9700;&#x8981;&#x7531;&#x6211;&#x63D0;&#x4F9B; Go SDK&#x3002;&#x672C;&#x7BC7;&#x8BB0;&#x5F55;&#x4E00;&#x4E0B;&#x6211;&#x4F5C;&#x4E3A;&#x65B0;&#x4EBA;&#x7684;&#x4E00;&#x4E9B;&#x63A2;&#x7D22;&#x5B66;&#x4E60;&#x8FC7;&#x7A0B;&#x3002;</p>
<h3 id="0-%E4%BD%95%E4%B8%BA-ffi">0. &#x4F55;&#x4E3A; FFI</h3>
<p>&#x521A;&#x63A5;&#x5230;&#x9700;&#x6C42;&#x6709;&#x70B9;&#x61F5;&#xFF0C;&#x8499;&#x853D;&#x5B8C;&#x4E86;&#x5148;&#x7406;&#x7406;&#x601D;&#x8DEF;&#x5427;&#x3002;&#x9996;&#x5148;&#x8FD9;&#x4FE9;&#x90FD;&#x662F;&#x9759;&#x6001;&#x7F16;&#x8BD1;&#x7684;&#x7F16;&#x7A0B;&#x8BED;&#x8A00;&#xFF0C;&#x6839;&#x636E;&#x6211;&#x4EE5;&#x524D;&#x778E;&#x6363;&#x9F13;&#x7535;&#x8111;&#x5F97;&#x51FA;&#x7684;&#x91CE;&#x9E21;&#x7ECF;&#x9A8C;&#xFF0C;&#x4EFB;&#x4F55;&#x8BED;&#x8A00;&#x7F16;&#x8BD1;&#x51FA;&#x6765;&#x7684;&#x4E8C;&#x8FDB;&#x5236;&#x4EA7;&#x7269;&#xFF0C;&#x672C;&#x8D28;&#x4E0A;&#x8DDF; C &#x8BED;&#x8A00;&#x7F16;&#x8BD1;&#x51FA;&#x6765;&#x7684;&#x6CA1;&#x533A;&#x522B;&#xFF0C;&#x8FD9;&#x662F;&#x56E0;&#x4E3A;&#x73B0;&#x5B58;&#x7684;&#x6D41;&#x884C;&#x64CD;&#x4F5C;&#x7CFB;&#x7EDF;&#x51E0;&#x4E4E;&#x90FD;&#x7528; C &#x6784;&#x9020;&#x800C;&#x6765;&#xFF0C;&#x6240;&#x4EE5;&#x5185;&#x5B58;&#x7BA1;&#x7406;&#x3001;&#x7CFB;&#x7EDF;&#x8C03;&#x7528;&#x7B49;&#x7B49;&#x57FA;&#x672C;&#x64CD;&#x4F5C;&#x90FD;&#x662F;&#x4EE5; C &#x4E3A;&#x57FA;&#x7840;&#x3002;&#x81EA;&#x7136;&#x5730;&#xFF0C;&#x52A8;&#x6001;&#x5E93;&#x4E4B;&#x95F4;&#x7684;&#x8C03;&#x7528;&#x4E5F;&#x80FD;&#x901A;&#x8FC7;&#x57FA;&#x7840;&#x7684; C &#x6807;&#x51C6;&#x6765;&#x505A;&#x5230;&#x3002;&#x7B80;&#x5355;&#x603B;&#x7ED3;&#x4E0B;&#x5C31;&#x662F;&#x6211;&#x4EEC;&#x628A;&#x539F;&#x6765;&#x7684; Rust &#x5E93;&#x5305;&#x88C5;&#x505A;&#x6210;&#x4E00;&#x4E2A; C &#x5E93;&#xFF0C;&#x518D;&#x7531; Go &#x53BB;&#x8C03;&#x7528;&#x8FD9;&#x4E2A; C &#x5E93;&#xFF0C;&#x5C31;&#x5B8C;&#x6210;&#x4E86;&#x8DE8;&#x8BED;&#x8A00;&#x8C03;&#x7528;&#xFF0C;&#x4E5F;&#x5C31;&#x662F;&#x6240;&#x8C13;&#x7684; FFI&#xFF0C;&#x5916;&#x90E8;&#x51FD;&#x6570;&#x63A5;&#x53E3;&#xFF0C;&#x5168;&#x79F0; Foreign Function Interface&#x3002;</p>
<h3 id="1-%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86">1. &#x5185;&#x5B58;&#x7BA1;&#x7406;</h3>
<p>&#x7EC6;&#x8282;&#x662F;&#x9B54;&#x9B3C;&#xFF0C;&#x672C;&#x7BC7;&#x5C31;&#x4ECE;&#x5165;&#x95E8;&#x7684;&#x5185;&#x5B58;&#x7BA1;&#x7406;&#x8BF4;&#x8D77;&#x3002;&#x6B63;&#x5E38;&#x7528;&#x5355;&#x4E00;&#x79CD;&#x56FE;&#x7075;&#x5B8C;&#x5907;&#x8BED;&#x8A00;&#x7F16;&#x8BD1;&#x6784;&#x5EFA;&#x51FA;&#x6765;&#x7684;&#x7A0B;&#x5E8F;&#xFF0C;&#x6BD4;&#x5982; go &#x6709;&#x4E2A;&#x975E;&#x5E38;&#x5F3A;&#x5927;&#x7684; runtime&#xFF0C;&#x81EA;&#x5E26;&#x5783;&#x573E;&#x56DE;&#x6536;&#xFF0C;&#x81EA;&#x52A8;&#x7BA1;&#x7406;&#x5185;&#x5B58;&#xFF0C;&#x4F7F;&#x7528;&#x8005;&#x57FA;&#x672C;&#x65E0;&#x9700;&#x64CD;&#x5FC3;&#x3002;&#x53EF;&#x4E00;&#x65E6;&#x6D89;&#x53CA; FFI &#x5C31;&#x4E0D;&#x4E00;&#x6837;&#x4E86;&#xFF0C;&#x6240;&#x6709;&#x7ECF;&#x8FC7;&#x4E86; FFI &#x8FB9;&#x754C;&#x7684;&#x6307;&#x9488;&#xFF0C;go &#x865A;&#x62DF;&#x673A;&#x65E0;&#x4ECE;&#x786E;&#x4FE1;&#x5B83;&#x662F;&#x5426;&#x53EF;&#x4EE5;&#x66F4;&#x6539;&#x662F;&#x5426;&#x53EF;&#x4EE5;&#x56DE;&#x6536;&#xFF0C;&#x751A;&#x81F3;&#x8FDE;&#x6570;&#x636E;&#x7ED3;&#x6784;&#x53EF;&#x80FD;&#x90FD;&#x4E0D;&#x786E;&#x5B9A;&#xFF0C;&#x53EA;&#x80FD;&#x5B8C;&#x5168;&#x4F9D;&#x8D56;&#x7528;&#x6237;&#x5728;&#x7F16;&#x8BD1;&#x65F6;&#x7ED9;&#x7684;&#x5934;&#x6587;&#x4EF6;&#xFF0C;&#x4E8B;&#x5148;&#x58F0;&#x660E;&#x597D;&#x6240;&#x6709;&#x7A7F;&#x8D8A; FFI &#x8FB9;&#x754C;&#x7684;&#x884C;&#x4E3A;&#x3002;</p>
<p>&#x55EF;&#x90A3;&#x4E5F;&#x8FD8;&#x597D;&#x5427;&#xFF0C;&#x7136;&#x800C;&#x63A5;&#x7740;&#x5185;&#x5B58;&#x6240;&#x6709;&#x6743;&#x95EE;&#x9898;&#x5C31;&#x6765;&#x4E86;&#xFF0C;&#x4E00;&#x5757;&#x5185;&#x5B58;&#x7531;&#x8C01;&#x7533;&#x8BF7;&#x3001;&#x7BA1;&#x7406;&#x3001;&#x56DE;&#x6536;&#x5462;&#xFF1F;&#x6CA1;&#x6709;&#x89C4;&#x5212;&#x968F;&#x610F;&#x8BFB;&#x5199;&#x7684;&#x8BDD;&#x53EF;&#x662F;&#x4F1A; segfault &#x6F2B;&#x5929;&#x98DE;&#x54E6;&#x3002;&#x5177;&#x4F53;&#x64CD;&#x4F5C;&#x8D77;&#x6765;&#x5404;&#x79CD;&#x7EC4;&#x5408;&#x6709;&#x4E03;&#x516B;&#x79CD;&#x505A;&#x6CD5;&#xFF0C;&#x4E0D;&#x8FC7;&#x57FA;&#x672C;&#x539F;&#x5219;&#x662F;&#x8C01;&#x7533;&#x8BF7;&#x8C01;&#x5C31;&#x6709;&#x62E5;&#x6709;&#x6743;&#xFF0C;&#x5C31;&#x7531;&#x8C01;&#x8D1F;&#x8D23;&#x56DE;&#x6536;&#xFF0C;&#x907F;&#x514D;&#x4E03;&#x624B;&#x516B;&#x811A;&#x5199;&#x574F;&#x5185;&#x5BB9;&#xFF0C;&#x5173;&#x952E;&#x662F;&#x9632;&#x6B62;&#x591A;&#x6B21; free &#x6216;&#x8005; free &#x540E;&#x518D;&#x6B21;&#x4F7F;&#x7528;&#x4E4B;&#x7C7B;&#x7684;&#x4E25;&#x91CD;&#x95EE;&#x9898;&#x3002;&#x5E38;&#x7528;&#x7684;&#x51E0;&#x79CD;&#x7BA1;&#x7406;&#x65B9;&#x5F0F;&#x53EF;&#x4EE5;&#x53C2;&#x8003;<a href="https://dev.to/kgrech/7-ways-to-pass-a-string-between-rust-and-c-4ieb">&#x8FD9;&#x7BC7;&#x535A;&#x6587;</a>&#xFF0C;&#x91CC;&#x9762;&#x901A;&#x8FC7;&#x8DE8; FFI &#x5B57;&#x7B26;&#x4E32;&#x7684;&#x4F8B;&#x5B50;&#x5C55;&#x793A;&#x4E86;&#x591A;&#x79CD;&#x7B56;&#x7565;&#xFF0C;&#x5305;&#x62EC;&#x63D0;&#x4F9B;&#x56DE;&#x8C03;&#xFF0C;&#x9884;&#x5148;&#x7533;&#x8BF7;&#x518D;&#x5199;&#x5165;&#x7B49;&#x7B49;&#x3002;&#x8FD9;&#x91CC;&#x91C7;&#x7528;&#x76F8;&#x5BF9;&#x6700;&#x7B80;&#x5355;&#x7684;&#x65B9;&#x5F0F;&#xFF1A;&#x6211;&#x5728; Rust &#x5E93;&#x540C;&#x65F6;&#x63D0;&#x4F9B;&#x5BF9;&#x8C61;&#x521B;&#x5EFA;&#x548C;&#x5BF9;&#x8C61;&#x56DE;&#x6536;&#x7684;&#x65B9;&#x6CD5;&#xFF0C;&#x7531; go &#x7AEF;&#x7684; caller &#x6309;&#x9700;&#x8C03;&#x7528;&#x3002;</p>
<h3 id="2-%E4%B8%BE%E4%B8%AA%E7%AE%80%E5%8D%95%E6%A0%97%E5%AD%90%F0%9F%8C%B0">2. &#x4E3E;&#x4E2A;&#x7B80;&#x5355;&#x6817;&#x5B50;&#x1F330;</h3>
<p>&#x4E0B;&#x9762;&#x4E3E;&#x4E2A;&#x7B80;&#x5355;&#x4F8B;&#x5B50;&#xFF1A;&#x6839;&#x636E; config &#x914D;&#x7F6E;&#x521B;&#x5EFA;&#x4E00;&#x4E2A; reader&#xFF0C;&#x8C03;&#x7528; reader &#x8BFB;&#x53D6; arrow &#x6570;&#x636E;&#xFF0C;&#x5E76;&#x901A;&#x8FC7; ffi &#x5BFC;&#x51FA;&#xFF0C;&#x4F7F;&#x7528;&#x5B8C;&#x6BD5;&#x540E;&#x56DE;&#x6536; reader&#x3002;</p>
<h4 id="21-rust-%E7%AB%AF">2.1 Rust &#x7AEF;</h4>
<p>&#x9996;&#x5148;&#x521B;&#x5EFA; Rust &#x7AEF;&#x7684; FFI &#x9798;&#x5C42;&#x5305;&#xFF0C;&#x5982;&#x679C; rust &#x5E93;&#x672C;&#x8EAB;&#x591F;&#x7B80;&#x5355;&#x7684;&#x8BDD;&#x53EF;&#x4EE5;&#x8003;&#x8651;&#x4E0B; cbindgen &#x4E4B;&#x7C7B;&#x7684;&#x65B9;&#x6848;&#xFF0C;&#x5E2E;&#x4F60;&#x81EA;&#x52A8;&#x751F;&#x6210; C FFI &#x7684;&#x9798;&#x5C42;&#x548C;&#x5934;&#x6587;&#x4EF6;&#x3002;&#x8FD9;&#x91CC;&#x521B;&#x5EFA;&#x4E00;&#x4E2A; <code>libexample</code> &#x52A8;&#x6001;&#x5E93;&#x5305;&#xFF0C;<code>Cargo.toml</code>&#xFF1A;</p>
<pre><code class="language-toml">[package]
name = &quot;example&quot;
version = &quot;0.0.1&quot;
edition = &quot;2021&quot;

[lib]
name = &quot;example&quot;
# &#x58F0;&#x660E;&#x4E3A;&#x5916;&#x90E8;&#x52A8;&#x6001;&#x5E93;
crate-type = [&quot;cdylib&quot;]

[dependency]
# &#x8FD9;&#x91CC;&#x628A;&#x771F;&#x6B63;&#x7684; rust &#x529F;&#x80FD;&#x6A21;&#x5757;&#x5F15;&#x5165;&#x8FDB;&#x6765;
rustlib = &quot;...&quot;
</code></pre>
<details>
    <summary>&#x521B;&#x5EFA; `lib.rs` &#x5165;&#x53E3;&#xFF0C;&#x70B9;&#x51FB;&#x5C55;&#x5F00;&#x4EE3;&#x7801;&#x548C;&#x8BF4;&#x660E;</summary>
<pre><code class="language-rust">use std::ffi::CStr;
use arrow::array::StructArray;
use arrow::ffi::{FFI_ArrowArray, FFI_ArrowSchema};
use arrow::record_batch::RecordBatch;

use rustlib::prelude::new_reader;

/// `#[repr(C)]` &#x8868;&#x793A;&#x8BA9;&#x7F16;&#x8BD1;&#x5668;&#x628A; Reader &#x7C7B;&#x578B;&#x7F16;&#x8BD1;&#x4E3A;&#x517C;&#x5BB9; C &#x5185;&#x5B58;&#x5BF9;&#x9F50;&#x5E03;&#x5C40;&#x7684; struct
#[repr(C)]
pub struct Reader {
    ...
}

/// `#[no_mangle]` &#x8868;&#x793A;&#x8BA9; rustc &#x4E0D;&#x8981;&#x6539;&#x5199;&#x51FD;&#x6570;&#x540D;&#xFF0C;&#x4E0D;&#x7136;&#x540E;&#x7EED;&#x94FE;&#x63A5;&#x5668;&#x627E;&#x4E0D;&#x5230;&#x51FD;&#x6570;&#x4F1A;&#x65E0;&#x6CD5;&#x8C03;&#x7528;
/// &#x8FD9;&#x91CC;&#x4E3A;&#x521B;&#x5EFA; reader &#x7684;&#x51FD;&#x6570;&#xFF0C;&#x8FD4;&#x56DE; reader &#x7684;&#x6307;&#x9488;
#[no_mangle]
pub extern &quot;C&quot; fn create_reader(config: *const libc::c_char) -&gt; *const Reader {
    // &#x8BFB;&#x53D6;&#x6765;&#x81EA; c &#x7684; char[] &#x5B57;&#x4E32;
    let config_str: &amp;str = unsafe { CStr::from_ptr(config as *mut _) }.to_str().unwarp();
    // &#x521B;&#x5EFA; reader&#xFF0C;&#x5E76;&#x901A;&#x8FC7; Box &#x5C06;&#x5B83;&#x5BFC;&#x51FA;&#x4E3A; ffi &#x6307;&#x9488;&#xFF0C;&#x76F8;&#x5F53;&#x4E8E;&#x628A;&#x8FD9;&#x4E2A;&#x5BF9;&#x8C61;&#x7684;&#x5185;&#x5B58;&#x6545;&#x610F;&#x6CC4;&#x6F0F;&#x51FA;&#x53BB;&#xFF0C;&#x8BE6;&#x60C5;&#x53C2;&#x8003; rust &#x6587;&#x6863;&#x5173;&#x4E8E; `Box::into_raw` &#x7684;&#x8BF4;&#x660E;
    let reader: Reader = new_reader(config_str);
    Box::into_raw(Box::new(reader)) as *const Reader
}

/// &#x8FD9;&#x91CC;&#x662F;&#x56DE;&#x6536; reader &#x7684;&#x51FD;&#x6570;&#xFF0C;&#x9700;&#x8981;&#x8C03;&#x7528;&#x65B9;&#x7528;&#x5B8C; reader &#x540E;&#x518D;&#x8C03;&#x8FD9;&#x4E2A;&#x6765;&#x56DE;&#x6536;&#x5185;&#x5B58;
#[no_mangle]
pub extern &quot;C&quot; fn free_reader(reader_ptr: *const Reader) {
    let _ = unsafe { Box::&lt;Reader&gt;::from_raw(reader_ptr as *mut _) };
}

/// &#x5B9A;&#x4E49;&#x8FD4;&#x56DE;&#x7ED3;&#x679C;&#x7684; struct
#[repr(C)]
pub struct Batch {
    schema: *const FFI_ArrowSchema,
    array: *const FFI_ArrowArray,
}

/// &#x4F7F;&#x7528; reader &#x8BFB;&#x53D6;&#x6587;&#x4EF6;&#x5E76;&#x5BFC;&#x51FA;&#x4E3A; arrow &#x6570;&#x7EC4;&#x6570;&#x636E;&#xFF0C;&#x8FD4;&#x56DE;&#x7ED3;&#x679C;&#x6570;&#x636E;&#x7684;&#x6307;&#x9488;
#[no_mangle]
pub extern &quot;C&quot; fn read_as_arrow(reader_ptr: *const Reader, path: *const libc::c_char) -&gt; *const Batch {
    // &#x53EA;&#x83B7;&#x53D6; reader &#x7684;&#x5F15;&#x7528;&#x800C;&#x4E0D;&#x56DE;&#x6536; reader&#xFF0C;&#x8C03;&#x7528;&#x65B9;&#x53EF;&#x80FD;&#x8FD8;&#x6CA1;&#x7528;&#x5B8C;
    let reader: &amp;Reader = unsafe { &amp;*(reader_ptr as *mut _) };
    let path_str = unsafe { CStr::from_ptr(path as *mut _) }.to_str().unwrap();

    // &#x8BFB;&#x53D6;&#x5E76;&#x5BFC;&#x51FA;&#x6570;&#x636E;
    let batch: &amp;RecordBatch = reader.arrow_from_path(path_str);
    let table_struct: StructArray = batch.into();
    let schema = FFI_ArrowSchema::try_from(table_struct.data_type()).unwrap();
    let array = FFI_ArrowArray::new(table_struct.data());
    Box::into_raw(Box::new(Batch {
        schema: Box::into_raw(Box::new(schema)),
        array: Box::into_raw(Box::new(array)),
    })) as *const Batch
}
</code></pre>
</details>
<p>&#x8FD9;&#x91CC;&#x6709;&#x4E2A;&#x7279;&#x6B8A;&#x7684;&#x5730;&#x65B9;&#xFF0C;&#x8FD4;&#x56DE;&#x7684; <code>Batch</code> &#x7ED3;&#x679C;&#x6211;&#x4EEC;&#x65E0;&#x987B;&#x518D;&#x63D0;&#x4F9B;&#x5BF9;&#x5E94;&#x7684;&#x56DE;&#x6536;&#x65B9;&#x6CD5;&#x3002;rust arrow-rs &#x5E93;&#x4E2D;&#x4E24;&#x4E2A;&#x7C7B; <code>FFI_ArrowSchema</code> &#x548C; <code>FFI_ArrowArray</code> &#x662F;&#x4E13;&#x95E8;&#x7528;&#x6765;&#x7A7F;&#x8D8A; FFI &#x754C;&#x9762;&#x7684;&#xFF08;&#x8FD9;&#x4FE9;&#x4E5F;&#x662F; arrow &#x6807;&#x51C6;&#x91CC;&#x5B9A;&#x4E49;&#x8981;&#x6C42;&#x7684;&#x5185;&#x5BB9;&#xFF09;&#xFF0C;&#x8FD9;&#x4E24;&#x79CD;&#x5BF9;&#x8C61;&#x4F20;&#x9001;&#x51FA; FFI &#x754C;&#x9762;&#x88AB;&#x8C03;&#x7528;&#x7AEF;&#x5BFC;&#x5165;&#x540E;&#xFF0C;&#x5C31;&#x7531;&#x8C03;&#x7528;&#x65B9;&#x53D6;&#x5F97;&#x6240;&#x6709;&#x6743;&#xFF0C;&#x7531;&#x8C03;&#x7528;&#x65B9;&#x7684; arrow &#x5E93;&#x81EA;&#x884C;&#x56DE;&#x6536;&#xFF0C;&#x4E0D;&#x9700;&#x8981;&#x6211;&#x4EEC;&#x5728;&#x521B;&#x5EFA;&#x7AEF;&#x64CD;&#x5FC3;&#x56DE;&#x6536;&#x4E86;&#x3002;</p>
<h4 id="22-header-%E5%A4%B4%E6%96%87%E4%BB%B6">2.2 header &#x5934;&#x6587;&#x4EF6;</h4>
<p>&#x5728; C &#x5934;&#x6587;&#x4EF6;&#x4E0A;&#x58F0;&#x660E;&#x521A;&#x521A;&#x5BFC;&#x51FA;&#x7684;&#x7C7B;&#x578B;&#x548C;&#x51FD;&#x6570;&#xFF0C;&#x521B;&#x5EFA; <code>example.h</code>&#xFF1A;</p>
<pre><code class="language-c">#include&lt;stddef.h&gt;
typedef struct Reader {
...
} Reader;

Reader* create_reader(char[] config);
void free_reader(Reader* reader_ptr);

#define FFI_ArrowSchema_ptr uintptr_t
#define FFI_ArrowArray_ptr uintptr_t
typedef struct Batch {
    FFI_ArrowSchema_ptr schema;
    FFI_ArrowArray_ptr array;
} Batch;

Batch* read_as_arrow(Reader* reader_ptr, char[] path);
</code></pre>
<h4 id="23-go-%E7%AB%AF">2.3 Go &#x7AEF;</h4>
<p>&#x63A5;&#x7740;&#x5728; Go &#x7AEF;&#x6211;&#x4EEC;&#x4E5F;&#x9700;&#x8981;&#x4E00;&#x4E2A; C &#x5E93;&#x7684;&#x9798;&#x5C42;&#xFF0C;&#x547D;&#x540D;&#x4E3A; <code>libexample-go</code>&#x3002;<code>go.mod</code>&#xFF1A;</p>
<pre><code class="language-go.mod">module github.com/examplellc/libexample-go

go 1.21.0

require (...)
</code></pre>
<p>&#x63A5;&#x7740;&#x65B0;&#x5EFA; <code>reader.go</code>&#xFF0C;&#x521B;&#x5EFA;&#x4E00;&#x4E2A; <code>Reader</code> &#x5305;&#x88C5;&#x7C7B;&#x578B;&#x3002;</p>
<details>
    <summary>&#x65B0;&#x5EFA; `reader.go` &#x5982;&#x4E0B;&#xFF0C;&#x70B9;&#x51FB;&#x67E5;&#x770B;&#x4EE3;&#x7801;</summary>
<pre><code class="language-go">package main

/*
#cgo LDFLAGS: -lexample -L.
#include &lt;stdlib.h&gt;
#include &lt;stddef.h&gt;
#include &quot;example.h&quot;
*/
import &quot;C&quot;
import (
	&quot;github.com/apache/arrow/go/v12/arrow&quot;
	&quot;github.com/apache/arrow/go/v12/arrow/cdata&quot;
	&quot;unsafe&quot;
)

type Reader struct {
	readerPtr *C.struct_Reader
}

func NewReader(config string) Reader {
	return Reader{
		readerPtr: C.create_reader(C.CString(config)),
	}
}

func (r *Reader) Close() {
	if r.readerPtr != nil {
		C.free_reader(r.readerPtr)
	}
	r.readerPtr = nil
}

func (r *Reader) ReadArrow(path string) arrow.Record {
	batchPtr := C.read_as_arrow(r.readerPtr, C.CString(path))
	return cdata.ImportCRecordBatch(
		(*cdata.CArrowArray)(unsafe.Pointer(batchPtr.array)),
		(*cdata.CArrowSchema)(unsafe.Pointer(batchPtr.schema)),
	)
}
</code></pre>
</details>
<p>&#x6709;&#x4E00;&#x4E9B;&#x503C;&#x5F97;&#x6CE8;&#x610F;&#x7684;&#x5730;&#x65B9;&#xFF0C;</p>
<ul>
<li>&#x9996;&#x5148;&#x8FD9;&#x4E2A;&#x5305;&#x662F;&#x4E2A; cgo &#x5305;&#xFF0C;&#x9700;&#x8981;&#x8C03;&#x7528;&#x5BF9;&#x5E94;&#x5E73;&#x53F0;&#x7684;&#x7F16;&#x8BD1;&#x5668;&#x548C;&#x8FDE;&#x63A5;&#x5668;&#xFF0C;&#x5F00;&#x53D1;&#x73AF;&#x5883;&#x4E0A;&#x9700;&#x8981;&#x989D;&#x5916;&#x5B89;&#x88C5;&#xFF1B;</li>
<li>&#x7136;&#x540E;&#x4E0A;&#x9762;&#x6E90;&#x7801;&#x4E2D;&#x5F15;&#x7528;&#x4E86;&#x4E00;&#x4E2A; go &#x5185;&#x7F6E;&#x7684; &quot;C&quot; &#x5047;&#x5305;&#xFF0C;&#x7D27;&#x6328;&#x7740;&#x5B83; import &#x524D;&#x9762;&#x7684;&#x6CE8;&#x91CA;&#x6709;&#x7279;&#x6B8A;&#x4F5C;&#x7528;&#xFF0C;&#x8981;&#x7528; C &#x7684;&#x8BED;&#x6CD5;&#x5199;&#x9700;&#x8981;&#x5BFC;&#x5165;&#x7684;&#x5934;&#xFF0C;&#x91CC;&#x9762;&#x7528; <code>#cgo</code> &#x5F00;&#x5934;&#x7684;&#x884C;&#x53EF;&#x4EE5;&#x5B9A;&#x4E49;&#x7F16;&#x8BD1;&#x53C2;&#x6570;&#x3002;&#x6BD4;&#x5982;&#x8FD9;&#x91CC;&#x5B9A;&#x4E49;&#x4E86;&#x7F16;&#x8BD1;&#x540E;&#x9700;&#x8981;&#x94FE;&#x63A5;&#x7684;&#x5E93;&#x540D;&#x79F0;&#xFF0C;&#x4EE5;&#x53CA;&#x641C;&#x7D22;&#x8DEF;&#x5F84;&#xFF1A;<pre><code class="language-go">#cgo LDFLAGS: -lexample -L.
</code></pre>
</li>
<li>&#x9700;&#x8981;&#x5F15;&#x7528;&#x5934;&#x6587;&#x4EF6;&#x5185; struct &#x7C7B;&#x578B;&#x65F6;&#xFF0C;&#x53EF;&#x4EE5;&#x7528; <code>C.struct_</code> &#x5F00;&#x5934;&#x7684;&#x683C;&#x5F0F;&#x5F15;&#x7528;&#xFF0C;&#x6CE8;&#x610F;&#x533F;&#x540D; struct &#x4E0D;&#x80FD;&#x8FD9;&#x6837;&#x5F15;&#x7528;&#xFF0C;&#x5FC5;&#x987B;&#x50CF; <code>typedef struct {} xxx;</code> &#x8FD9;&#x6837;&#x5BFC;&#x51FA;&#x540E;&#x624D;&#x53EF;&#x7528; <code>C.struct_xxx</code>&#xFF1B;</li>
<li>&#x8981;&#x5F15;&#x7528;&#x5934;&#x6587;&#x4EF6;&#x5185;&#x58F0;&#x660E;&#x7684;&#x51FD;&#x6570;&#xFF0C;&#x4F7F;&#x7528; <code>C.read_as_arrow</code> &#x8FD9;&#x6837;&#x7684;&#x683C;&#x5F0F;&#x5373;&#x53EF;&#xFF1B;</li>
<li>&#x5047;&#x5305; &quot;C&quot; &#x8FD8;&#x5305;&#x542B;&#x4E86;&#x4E00;&#x4E9B;&#x5DE5;&#x5177;&#x51FD;&#x6570;&#xFF0C;&#x5177;&#x4F53;&#x53EF;&#x53C2;&#x8003; cgo &#x5B98;&#x65B9;&#x6587;&#x6863;&#xFF0C;&#x50CF;&#x8FD9;&#x91CC;&#x7528;&#x5230;&#x7684; <code>C.CString</code> &#x80FD;&#x628A; go string &#x8F6C;&#x6210; C &#x4E2D;&#x4EE5; null &#x7ED3;&#x5C3E;&#x7684; <code>char[]</code> &#x5B57;&#x4E32;&#xFF1B;</li>
</ul>
<p>&#x81F3;&#x6B64;&#x6240;&#x6709;&#x5305;&#x88C5;&#x5C42;&#x7684;&#x5305;&#x90FD;&#x5236;&#x4F5C;&#x5B8C;&#x6BD5;&#x4E86;&#x3002;</p>
<h4 id="24-%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95">2.4 &#x4F7F;&#x7528;&#x65B9;&#x6CD5;</h4>
<p>&#x63A5;&#x4E0B;&#x6765;&#x5728;&#x6B63;&#x5E38; go &#x9879;&#x76EE;&#x4E2D;&#x5982;&#x4F55;&#x8C03;&#x7528;&#x4E0A;&#x9762;&#x8FD9;&#x4E2A;&#x5E93;&#x5462;&#xFF1F;</p>
<ul>
<li>&#x9996;&#x5148; <code>cargo build --release</code> &#x7F16;&#x8BD1;&#x51FA; c &#x5E93;&#x672C;&#x4F53; <code>libexample.so</code>/<code>libexample.dylib</code>/<code>libexample.dll</code>&#xFF1B;</li>
<li>&#x628A;&#x4EE5;&#x4E0A;&#x7F16;&#x8BD1;&#x4EA7;&#x7269;&#x62F7;&#x8D1D;&#x5230;&#x5305;&#x7684;&#x6839;&#x76EE;&#x5F55;&#xFF0C;&#x56E0;&#x4E3A;&#x6211;&#x4EEC;&#x521A;&#x6307;&#x5B9A;&#x4E86;&#x94FE;&#x63A5;&#x5E93;&#x7684;&#x641C;&#x7D22;&#x8DEF;&#x5F84; <code>-L.</code>&#xFF0C;&#x5F53;&#x7136;&#x4E5F;&#x53EF;&#x4EE5;&#x653E;&#x5230;&#x5E38;&#x7528;&#x7684; <code>/usr/local/lib</code> &#x7B49;&#x76EE;&#x5F55;&#x4E0B;&#xFF1B;</li>
<li>&#x6700;&#x540E; go &#x7F16;&#x8BD1;&#x65F6;&#x6253;&#x5F00; cgo &#x5F00;&#x5173;&#xFF0C;<code>CGO_ENABLED=1 go build . -o build/main</code> &#x50CF;&#x8FD9;&#x6837;&#x7528;&#x73AF;&#x5883;&#x53D8;&#x91CF; <code>CGO_ENABLED</code> &#x5373;&#x53EF;&#xFF1B;</li>
</ul>
<p>&#x4E0D;&#x51FA;&#x610F;&#x5916;&#xFF0C;<code>build/main</code> &#x5DF2;&#x7ECF;&#x53EF;&#x4EE5;&#x6B63;&#x5E38;&#x6267;&#x884C;&#x4E86;&#x3002;</p>
<h3 id="3-debug">3. Debug</h3>
<p>&#x4E0A;&#x9762;&#x5F00;&#x4E2A;&#x73A9;&#x7B11;&#x54C8;&#x54C8;&#xFF0C;&#x7B2C;&#x4E00;&#x6B21;&#x8FD0;&#x884C;&#x4E0D;&#x51FA;&#x610F;&#x5916;&#x90A3;&#x662F;&#x4E0D;&#x53EF;&#x80FD;&#x7684;&#x1F61D;&#x7A0B;&#x5E8F;&#x8D77;&#x6765;&#x94C1;&#x5B9A;&#x5C11;&#x4E0D;&#x4E86; segfaults&#xFF0C;&#x8FD8;&#x6709;&#x5404;&#x79CD;&#x5947;&#x5947;&#x602A;&#x602A;&#x7684;&#x6570;&#x636E;&#x9519;&#x4E71;&#xFF0C;&#x5EFA;&#x8BAE;&#x591A;&#x6253;&#x65AD;&#x70B9;&#x591A;&#x8C03;&#x8BD5;&#xFF0C;&#x5584;&#x7528; gdb&#xFF0C;&#x8FD8;&#x6709; go delv &#x548C; rust lldb&#xFF0C;&#x4E5F;&#x662F;&#x4E0D;&#x9519;&#x7684;&#x5B98;&#x65B9;&#x8C03;&#x8BD5;&#x5DE5;&#x5177;&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[GO 低内存极速 I/O 编程：无缓冲编码文件，同时计算哈希值一并上传]]></title><description><![CDATA[Golang 自带了内容相当丰富的 io 库，对 io 编程友好。最近碰到个需要发送文件的 REST api，文件内容需 base64 编码为文本，同时还要文件的 md5sum，再附加一系列其他信息，最后用 json 文本发送。最无脑的做法是将整个文件读入缓冲区，依次计算 md5sum，再把文件编码为 base64 文本，最后拼接所需的 json 文本完成发送。当然这样文件一大铁定崩内存💩，显然有更合适的做法……]]></description><link>https://xcel.me/go-fast-io-no-buffering-http-complex-post/</link><guid isPermaLink="false">6499b869b9aecb71c91c20ee</guid><category><![CDATA[Go]]></category><category><![CDATA[programming]]></category><category><![CDATA[I/O]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Thu, 29 Jun 2023 17:23:46 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1550613095-bb19738b8b4b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDQ5fHxuZXR3b3JrfGVufDB8fHx8MTY4ODA1NDY3M3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1550613095-bb19738b8b4b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wxMTc3M3wwfDF8c2VhcmNofDQ5fHxuZXR3b3JrfGVufDB8fHx8MTY4ODA1NDY3M3ww&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=2000" alt="GO &#x4F4E;&#x5185;&#x5B58;&#x6781;&#x901F; I/O &#x7F16;&#x7A0B;&#xFF1A;&#x65E0;&#x7F13;&#x51B2;&#x7F16;&#x7801;&#x6587;&#x4EF6;&#xFF0C;&#x540C;&#x65F6;&#x8BA1;&#x7B97;&#x54C8;&#x5E0C;&#x503C;&#x4E00;&#x5E76;&#x4E0A;&#x4F20;"><p>Golang &#x81EA;&#x5E26;&#x4E86;&#x5185;&#x5BB9;&#x76F8;&#x5F53;&#x4E30;&#x5BCC;&#x7684; io &#x5E93;&#xFF0C;&#x5BF9; io &#x7F16;&#x7A0B;&#x53CB;&#x597D;&#x3002;&#x6700;&#x8FD1;&#x78B0;&#x5230;&#x4E2A;&#x9700;&#x8981;&#x53D1;&#x9001;&#x6587;&#x4EF6;&#x7684; REST api&#xFF0C;&#x6587;&#x4EF6;&#x5185;&#x5BB9;&#x9700; base64 &#x7F16;&#x7801;&#x4E3A;&#x6587;&#x672C;&#xFF0C;&#x540C;&#x65F6;&#x8FD8;&#x8981;&#x6587;&#x4EF6;&#x7684; md5sum&#xFF0C;&#x518D;&#x9644;&#x52A0;&#x4E00;&#x7CFB;&#x5217;&#x5176;&#x4ED6;&#x4FE1;&#x606F;&#xFF0C;&#x6700;&#x540E;&#x7528; json &#x6587;&#x672C;&#x53D1;&#x9001;&#x3002;&#x6700;&#x65E0;&#x8111;&#x7684;&#x505A;&#x6CD5;&#x662F;&#x5C06;&#x6574;&#x4E2A;&#x6587;&#x4EF6;&#x8BFB;&#x5165;&#x7F13;&#x51B2;&#x533A;&#xFF0C;&#x4F9D;&#x6B21;&#x8BA1;&#x7B97; md5sum&#xFF0C;&#x518D;&#x628A;&#x6587;&#x4EF6;&#x7F16;&#x7801;&#x4E3A; base64 &#x6587;&#x672C;&#xFF0C;&#x6700;&#x540E;&#x62FC;&#x63A5;&#x6240;&#x9700;&#x7684; json &#x6587;&#x672C;&#x5B8C;&#x6210;&#x53D1;&#x9001;&#x3002;&#x5F53;&#x7136;&#x8FD9;&#x6837;&#x6587;&#x4EF6;&#x4E00;&#x5927;&#x94C1;&#x5B9A;&#x5D29;&#x5185;&#x5B58;&#x1F4A9;&#xFF0C;&#x663E;&#x7136;&#x6709;&#x66F4;&#x5408;&#x9002;&#x7684;&#x505A;&#x6CD5;&#x2026;&#x2026;</p>
<p>&#x7A0D;&#x7A0D;&#x597D;&#x4E00;&#x70B9;&#x7684;&#x505A;&#x6CD5;&#x662F;&#x4ECE;&#x5934;&#x5230;&#x5C3E;&#x8BFB;&#x53D6;&#x4E00;&#x6B21;&#x6587;&#x4EF6;&#x8BA1;&#x7B97; md5sum&#xFF0C;&#x518D; seek &#x56DE;&#x6587;&#x4EF6;&#x5934;&#x90E8;&#x518D;&#x4ECE;&#x5934;&#x5230;&#x5C3E;&#x8BFB;&#x4E00;&#x6B21;&#x5B8C;&#x6210;&#x7F16;&#x7801;&#xFF0C;&#x6700;&#x540E;&#x62FC;&#x63A5; json &#x6587;&#x672C;&#x5E76;&#x53D1;&#x9001;&#x3002;&#x8FD9;&#x79CD;&#x505A;&#x6CD5;&#x7701;&#x6389;&#x4E86;&#x4E00;&#x6B21;&#x6587;&#x4EF6;&#x8BFB;&#x53D6;&#x7684;&#x7F13;&#x51B2;&#xFF0C;&#x4F46;&#x8FD8;&#x6709; base64 &#x7F16;&#x7801;&#x7684;&#x7F13;&#x51B2;&#x6CA1;&#x6709;&#x53BB;&#x6389;&#xFF0C;&#x56E0;&#x4E3A;&#x6700;&#x540E;&#x62FC;&#x63A5; json &#x7684;&#x65F6;&#x5019;&#x4ECD;&#x7136;&#x9700;&#x8981;&#x5B8C;&#x6574;&#x7684;&#x5185;&#x5BB9;&#xFF0C;&#x4ECD;&#x4F1A;&#x5360;&#x7528;&#x5927;&#x5757;&#x5185;&#x5B58;&#xFF0C;&#x6587;&#x4EF6;&#x4E00;&#x5927;&#x8FD8;&#x662F;&#x4F1A;&#x5D29;&#x3002;&#x6B64;&#x5904;&#x7684;&#x6E90;&#x7801;&#x4EE5;&#x53CA;&#x8FD0;&#x884C;&#x6548;&#x679C;&#x5982;&#x4E0B;&#xFF0C;&#x5176;&#x4E2D; <code>echo.go</code> &#x4E3A;&#x6A21;&#x62DF;&#x7684; API &#x670D;&#x52A1;&#x5668;&#xFF0C;&#x4F1A;&#x539F;&#x6837;&#x8FD4;&#x56DE;&#x5BA2;&#x6237;&#x7AEF;&#x4E0A;&#x4F20;&#x7684;&#x6570;&#x636E;&#xFF0C;<code>upload.go</code> &#x4E3A;&#x672C;&#x7A0B;&#x5E8F;&#xFF1A;</p>
<details>
    <summary>&#x70B9;&#x51FB;&#x5C55;&#x5F00;&#x67E5;&#x770B;/&#x6298;&#x53E0;&#x6E90;&#x7801; `upload.go`</summary>
<pre><code class="language-go">package main

import (
	&quot;bytes&quot;
	&quot;crypto/md5&quot;
	&quot;encoding/base64&quot;
	&quot;encoding/json&quot;
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;log&quot;
	&quot;net/http&quot;
	&quot;os&quot;
)

func main() {
	f, err := os.Open(&quot;./testFile.bin&quot;)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

    // &#x8BA1;&#x7B97; md5sum
	hasher := md5.New()
	_, err = io.Copy(hasher, f)
	if err != nil {
		log.Fatal(err)
	}
	md5sum := fmt.Sprintf(&quot;%x&quot;, hasher.Sum(nil))

    // &#x6587;&#x4EF6;&#x56DE;&#x9000;&#x5230;&#x5934;&#x90E8;
	_, err = f.Seek(0, io.SeekStart)
	if err != nil {
		log.Fatal(err)
	}

    // base64 &#x7F16;&#x7801;&#x6587;&#x4EF6;&#xFF0C;&#x7F16;&#x7801;&#x540E;&#x7684;&#x6587;&#x672C;&#x5B58;&#x5165; encoded
	encoded := bytes.NewBuffer(nil)
	encoder := base64.NewEncoder(base64.StdEncoding, encoded)
	buf := make([]byte, 1024)
	for {
		n, err := f.Read(buf)
		if err != nil &amp;&amp; err != io.EOF {
			log.Fatal(err)
		}
		if n == 0 {
			break
		}
		encoder.Write(buf[:n])
	}
	encoder.Close()

    // &#x62FC;&#x63A5; json
	jsonBytes, err := json.Marshal(map[string]string{
		&quot;md5&quot;:  md5sum,
		&quot;file&quot;: encoded.String(),
	})
	if err != nil {
		log.Fatal(err)
	}

    // &#x53D1;&#x9001;&#x5230;&#x670D;&#x52A1; API
	httpClient := &amp;http.Client{}
	res, err := httpClient.Post(
		&quot;http://127.0.0.1:8081/&quot;,
		&quot;application/json&quot;,
		bytes.NewBuffer(jsonBytes),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

    // &#x8BFB;&#x53D6;&#x670D;&#x52A1;&#x54CD;&#x5E94;
	bodyBytes, err := io.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	log.Printf(
		&quot;client: response Status: %s\n&quot;+
			&quot;body length: %d\n&quot;+
			&quot;body preview: %s&quot;,
		res.Status, len(bodyBytes), string(bodyBytes[:100]),
	)
}
</code></pre>
</details>
<details>
    <summary>&#x70B9;&#x51FB;&#x5C55;&#x5F00;/&#x6536;&#x7F29; `upload.go` &#x8FD0;&#x884C;&#x6548;&#x679C;</summary>
<script async id="asciicast-1QK37E6u8t9ZVJQgDQbZhw62I" src="https://asciinema.org/a/1QK37E6u8t9ZVJQgDQbZhw62I.js"></script>
</details>
<p>&#x4E0B;&#x9762;&#x662F;&#x672C;&#x7BC7;&#x8981;&#x4ECB;&#x7ECD;&#x7684;&#x65B9;&#x6CD5;&#xFF0C;&#x65E0;&#x9700;&#x989D;&#x5916;&#x7684;&#x5927;&#x7F13;&#x51B2;&#x533A;&#xFF0C;&#x901A;&#x8FC7;&#x7ED3;&#x5408;&#x4F7F;&#x7528; go &#x81EA;&#x5E26;&#x7684; <code>io.Pipe</code>&#x3001;<code>io.TeeReader</code>&#x3001;<code>io.MultiReader</code>&#x3001;<code>io.Copy</code> &#x7B49;&#xFF0C;&#x7528;&#x4E00;&#x6B21;&#x6587;&#x4EF6;&#x8BFB;&#x53D6;&#x5B8C;&#x6210;&#x4E86;&#x6240;&#x6709;&#x64CD;&#x4F5C;&#x3002;&#x5176;&#x4E2D; <code>io.TeeReader</code> &#x5728;&#x8BFB;&#x53D6;&#x6587;&#x4EF6; Reader &#x7684;&#x540C;&#x65F6;&#xFF0C;&#x628A;&#x5185;&#x5BB9;&#x6284;&#x5199;&#x5230;&#x53E6;&#x4E00;&#x4E2A; md5 hasher &#x91CC;&#x6765;&#x8BA1;&#x7B97; md5sum &#x503C;&#xFF1B;&#x63A5;&#x7740;&#x518D;&#x6284;&#x5199;&#x5230; base64 &#x7F16;&#x7801;&#x5668;&#x4E2D;&#xFF0C;&#x540C;&#x65F6;&#x7531; <code>io.Pipe</code> &#x5B8C;&#x6210;&#x5199;&#x8F6C;&#x8BFB;&#xFF08;&#x800C;&#x4E0D;&#x662F;&#x7528;&#x7F13;&#x51B2;&#x533A;&#xFF09;&#x5F97;&#x5230;&#x4E00;&#x4E2A;&#x8BFB;&#x53D6;&#x7F16;&#x7801;&#x540E;&#x5185;&#x5BB9;&#x7684; reader&#xFF0C;&#x6700;&#x540E;&#x7528; <code>io.MultiReader</code> &#x628A;&#x5404;&#x4E2A; reader &#x4E32;&#x63A5;&#x51FA;&#x5355;&#x4E00; reader <code>chained</code>&#xFF0C;<code>http.Client.Post</code> &#x7528;&#x5B83;&#x4E00;&#x6B21;&#x4FBF;&#x80FD;&#x8BFB;&#x53D6;&#x5230;&#x5B8C;&#x6574;&#x5185;&#x5BB9;&#x7684; json&#xFF0C;&#x65E0;&#x9700;&#x4EFB;&#x4F55;&#x7F13;&#x51B2;&#x533A;&#x76F4;&#x63A5;&#x53D1;&#x9001;&#x3002;&#x5927;&#x4F53;&#x6D41;&#x7A0B;&#x793A;&#x610F;&#x56FE;&#x548C;&#x8BE6;&#x7EC6;&#x4EE3;&#x7801;&#x5982;&#x4E0B;&#x3002;</p>
<details>
    <summary>&#x70B9;&#x51FB;&#x5C55;&#x5F00;/&#x6298;&#x53E0; `upload2.go`</summary>
<pre><code class="language-go">func upload2() {
	f, err := os.Open(&quot;./testFile.bin&quot;)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	hasher := md5.New()
	copiedReader := io.TeeReader(f, hasher)
	hashBuffer := bytes.NewBuffer(nil)

	pr, pw := io.Pipe()
	encoder := base64.NewEncoder(base64.StdEncoding, pw)
	encoderDoneChan := make(chan interface{})
	go func() {
		_, err := io.Copy(encoder, copiedReader)
		if err != nil {
			pw.CloseWithError(err)
			log.Fatal(err)
		} else {
			pw.Close()
		}
		encoder.Close()
		hashBuffer.WriteString(fmt.Sprintf(&quot;%x&quot;, hasher.Sum(nil)))
		close(encoderDoneChan)
	}()

	jsonHead := bytes.NewReader([]byte(`{&quot;fileContent&quot;:&quot;`))
	jsonHead2 := bytes.NewReader([]byte(`&quot;,&quot;md5&quot;:&quot;`))
	jsonTail := bytes.NewReader([]byte(`&quot;}`))
	chained := io.MultiReader(jsonHead, pr, jsonHead2, hashBuffer, jsonTail)

	httpClient := &amp;http.Client{}
	res, err := httpClient.Post(
		&quot;http://127.0.0.1:8081/&quot;,
		&quot;application/json&quot;,
		chained,
	)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
} 
</code></pre>
</details>
<details>
    <summary>&#x70B9;&#x51FB;&#x5C55;&#x5F00;/&#x6298;&#x53E0;&#x67E5;&#x770B;&#x793A;&#x610F;&#x56FE;</summary>
<img src="https://xcel.me/content/images/2023/06/03F67AEA-44F0-4D96-B42E-25CC0630141D_1_105_c.jpeg" alt="GO &#x4F4E;&#x5185;&#x5B58;&#x6781;&#x901F; I/O &#x7F16;&#x7A0B;&#xFF1A;&#x65E0;&#x7F13;&#x51B2;&#x7F16;&#x7801;&#x6587;&#x4EF6;&#xFF0C;&#x540C;&#x65F6;&#x8BA1;&#x7B97;&#x54C8;&#x5E0C;&#x503C;&#x4E00;&#x5E76;&#x4E0A;&#x4F20;">
</details><!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[如何在 Gitlab CI 中加速 Rust 编译]]></title><description><![CDATA[在 Dockerfile 中加入黑魔法，加速 CI 上 Rust 项目的编译…]]></description><link>https://xcel.me/gitlab-ci-zhong-gei-cargo-jia-su-bian-yi/</link><guid isPermaLink="false">64453141b9aecb71c91c1e50</guid><category><![CDATA[Docker]]></category><category><![CDATA[Cargo]]></category><category><![CDATA[Gitlab]]></category><category><![CDATA[CI]]></category><category><![CDATA[Hack]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Thu, 22 Jun 2023 09:26:40 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>&#x6700;&#x8FD1;&#x56E2;&#x961F; gitlab &#x4E0A;&#x65B0;&#x52A0;&#x5165;&#x4E86;&#x4E00;&#x4E2A; rust &#x9879;&#x76EE;&#xFF0C;&#x53D1;&#x73B0; cargo &#x6784;&#x5EFA;&#x8D77;&#x6765;&#x975E;&#x5E38;&#x6162;&#xFF0C;&#x7531;&#x4E8E; crates &#x662F;&#x7528;&#x6E90;&#x7801;&#x5206;&#x53D1;&#xFF0C;&#x5982;&#x679C;&#x6CA1;&#x6709;&#x4E2D;&#x95F4;&#x4EA7;&#x7269;&#x7684;&#x7F13;&#x5B58;&#xFF0C;cargo &#x6BCF;&#x56DE;&#x90FD;&#x8981;&#x628A;&#x6240;&#x6709;&#x4F9D;&#x8D56;&#x91CD;&#x65B0;&#x7F16;&#x8BD1;&#x4E00;&#x904D;&#xFF0C;&#x56E0;&#x6B64;&#x6CA1;&#x6709;&#x7F13;&#x5B58;&#x65F6;&#x5C31;&#x4F1A;&#x901F;&#x5EA6;&#x582A;&#x5FE7;&#x3002;&#x53EF;&#x662F; CI &#x5BB9;&#x5668;&#x73AF;&#x5883;&#x76F8;&#x5F53;&#x590D;&#x6742;&#x3002;&#x600E;&#x6837;&#x5728; gitlab ci runner &#x4E0A;&#x914D;&#x7F6E; cargo &#x7684;&#x7F13;&#x5B58;&#x5462;&#xFF1F;</p>
<p>&#x57FA;&#x672C;&#x601D;&#x8DEF;&#x6709;&#x4E09;&#x4E2A;&#xFF1A;</p>
<ul>
<li>gitlab ci runner &#x81EA;&#x5E26;&#x7684;&#x7F13;&#x5B58;&#xFF0C;&#x5728; <code>.gitlab-ci.yml</code> &#x4E2D;&#x52A0;&#x5165; <code>cache:</code> &#x914D;&#x7F6E;&#xFF0C;<a href="https://docs.gitlab.com/ee/ci/yaml/#cache">https://docs.gitlab.com/ee/ci/yaml/#cache</a></li>
<li>docker buildkit &#x63D0;&#x4F9B;&#x7684;&#x7F13;&#x5B58;&#xFF0C;&#x5373;&#x5728; dockerfile &#x4E2D;&#x52A0;&#x5165; <code>RUN --mount=type=cache</code> <a href="https://docs.docker.com/engine/reference/builder/#run---mounttypecache">https://docs.docker.com/engine/reference/builder/#run---mounttypecache</a></li>
<li>&#x5408;&#x7406;&#x62C6;&#x5206; dockerfile &#x4E2D;&#x7684;&#x6B65;&#x9AA4;&#xFF0C;&#x8C03;&#x6574;&#x64CD;&#x4F5C;&#x987A;&#x5E8F;&#xFF0C;&#x628A;&#x53D8;&#x66F4;&#x5C11;&#x7684;&#x6B65;&#x9AA4;&#x96C6;&#x4E2D;&#x5230;&#x524D;&#x6392;&#xFF0C;&#x53D8;&#x66F4;&#x591A;&#x7684;&#x64CD;&#x4F5C;&#x9760;&#x540E;&#xFF0C;&#x6700;&#x5927;&#x5229;&#x7528; docker build &#x5C42;&#x95F4;&#x7F13;&#x5B58;&#xFF1B;</li>
</ul>
<p>&#x9996;&#x5148;&#x7B2C;&#x4E00;&#x4E2A;&#xFF0C;&#x914D;&#x7F6E;&#x5199;&#x5230;&#x4E00;&#x822C;&#x5C31;&#x53D1;&#x73B0;&#x95EE;&#x9898;&#x4E86;&#x3002;&#x56E0;&#x4E3A; gitlab ci &#x4F7F;&#x7528; docker &#x8FD0;&#x884C;&#xFF0C;cargo &#x9879;&#x76EE;&#x7F16;&#x8BD1;&#x53C8;&#x4F7F;&#x7528; docker &#x5BB9;&#x5668;&#xFF0C;&#x6240;&#x4EE5;&#x5B58;&#x5728; dind&#xFF08;docker in docker) &#x5BB9;&#x5668;&#x5D4C;&#x5957;&#x7684;&#x72B6;&#x51B5;&#xFF0C;&#x5728; ci runner &#x5916;&#x5C42;&#x5BB9;&#x5668;&#x4E2D;&#x914D;&#x7F6E;&#x7684;&#x7F13;&#x5B58;&#x76EE;&#x5F55;&#x65E0;&#x6CD5;&#x88AB;&#x5185;&#x5C42;&#x5BB9;&#x5668;&#x4F7F;&#x7528;&#x3002;&#x8FD9;&#x79CD;&#x65B9;&#x6CD5;&#x53EA;&#x9002;&#x7528;&#x4E8E;&#x7B80;&#x5355;&#x7684; ci &#x6D41;&#x7A0B;&#xFF0C;ci job &#x5185;&#x90E8;&#x6709;&#x7528;&#x5230; docker &#x65F6;&#x65E0;&#x80FD;&#x4E3A;&#x529B;&#x3002;</p>
<p>&#x7B2C;&#x4E8C;&#x4E2A;&#x662F;&#x4F7F;&#x7528;&#x4E86; docker &#x7684;&#x7F13;&#x5B58;&#x673A;&#x5236;&#xFF0C;&#x5916;&#x6302;&#x4E00;&#x4E2A;&#x4E34;&#x65F6;&#x5B58;&#x50A8;&#x5377;&#x6302;&#x5230;&#x6307;&#x5B9A;&#x76EE;&#x5F55;&#xFF0C;&#x53EF;&#x4EE5;&#x5728;&#x591A;&#x6B21; build &#x4E4B;&#x95F4;&#x5171;&#x4EAB;&#x7F13;&#x5B58;&#xFF0C;&#x7406;&#x8BBA;&#x4E0A;&#x53EA;&#x8981; ci runner &#x5BBF;&#x4E3B;&#x6709;&#x8DB3;&#x591F;&#x7A7A;&#x95F4;&#x7F13;&#x5B58;&#x5C31;&#x4E0D;&#x4F1A;&#x88AB;&#x56DE;&#x6536;&#xFF0C;&#x6548;&#x679C;&#x5E94;&#x8BE5;&#x4E0D;&#x9519;&#xFF0C;&#x4F46;&#x9700;&#x8981; docker &#x7248;&#x672C;&#x652F;&#x6301;&#x3002;</p>
<p>&#x7B2C;&#x4E09;&#x4E2A;&#x5C42;&#x95F4;&#x7F13;&#x5B58;&#x662F;&#x6700;&#x7B80;&#x5355;&#x7684;&#xFF0C;&#x4F46;&#x53EF;&#x80FD;&#x6548;&#x679C;&#x6709;&#x9650;&#x3002;&#x8C03;&#x6574;&#x4E00;&#x4E0B; dockerfile &#x4E2D;&#x7684;&#x547D;&#x4EE4;&#x5373;&#x53EF;&#xFF0C;&#x6280;&#x5DE7;&#x662F;&#x628A;&#x957F; RUN &#x6B65;&#x9AA4;&#x62C6;&#x5206;&#x6210;&#x591A;&#x4E2A;&#x77ED; RUN &#x6B65;&#x9AA4;&#xFF0C;&#x6587;&#x4EF6;&#x53D8;&#x66F4;&#x5C11;&#x7684;&#x9760;&#x524D;&#x653E;&#xFF0C;&#x53D8;&#x66F4;&#x6700;&#x591A;&#x7684;&#x7F16;&#x8BD1;&#x6B65;&#x9AA4;&#x653E;&#x6700;&#x540E;&#x3002;&#x8FD9;&#x6837; docker &#x670D;&#x52A1;&#x68C0;&#x6D4B;&#x5230;&#x524D;&#x9762;&#x6CA1;&#x6709;&#x53D8;&#x66F4;&#x5C31;&#x4F1A;&#x8DF3;&#x8FC7;&#x6267;&#x884C;&#xFF0C;&#x76F4;&#x63A5;&#x4F7F;&#x7528;&#x5DF2;&#x6709;&#x7684;&#x5C42;&#x3002;</p>
<p>&#x5B9E;&#x9645;&#x64CD;&#x4F5C;&#x65F6;&#xFF0C;&#x7B2C;&#x4E8C;&#x79CD;&#x65B9;&#x5F0F;&#x6211;&#x53D1;&#x73B0;&#x5B8C;&#x5168;&#x6CA1;&#x6709;&#x6548;&#x679C;&#xFF08;&#x5177;&#x4F53;&#x539F;&#x56E0;&#x5FD8;&#x8BB0;&#x4E86;&#xFF0C;&#x597D;&#x50CF;&#x662F; gitlab &#x7248;&#x672C;&#x4E0D;&#x652F;&#x6301;&#xFF09;&#x3002;&#x6700;&#x540E;&#x4F7F;&#x7528;&#x7B2C;&#x4E09;&#x79CD;&#x65B9;&#x5F0F;&#xFF0C;&#x53C8;&#x53E6;&#x5916;&#x4E86;&#x4E00;&#x70B9;&#x70B9; cargo &#x9ED1;&#x9B54;&#x6CD5;&#x540E;&#x6709;&#x5947;&#x6548;&#x2026;&#x2026; &#x5177;&#x4F53;&#x6765;&#x8BF4;&#x662F;&#x8FD9;&#x6837;&#x7684;&#xFF0C;&#x8FD9;&#x662F;&#x6B63;&#x5E38;&#x7684; Dockerfile&#xFF1A;</p>
<pre><code class="language-dockerfile">FROM ... as builder
WORKDIR /workdir
RUN mkdir -p ./.cargo
COPY config.toml ./.cargo/
COPY ./ ./
RUN cargo update
RUN cargo build --release --bin main
...
</code></pre>
<p>&#x53EF;&#x4EE5;&#x770B;&#x5230;&#x5C31;&#x662F;&#x6B63;&#x5E38;&#x7684;&#x5EFA;&#x7ACB;&#x5DE5;&#x4F5C;&#x76EE;&#x5F55;&#x3001;&#x62F7;&#x8D1D;&#x914D;&#x7F6E;&#x3001;&#x6E90;&#x7801;&#x3001;&#x7F16;&#x8BD1;&#x7ED3;&#x675F;&#x3002;&#x4F46;&#x662F;&#x95EE;&#x9898;&#x5728;&#x4E8E; <code>cargo build</code> &#x662F;&#x628A;&#x9879;&#x76EE;&#x4F9D;&#x8D56;&#x548C;&#x5B9E;&#x9645;&#x4EE3;&#x7801;&#x4E00;&#x5757;&#x7F16;&#x8BD1;&#x7684;&#xFF0C;&#x54EA;&#x6015;&#x9879;&#x76EE;&#x4EE3;&#x7801;&#x53EA;&#x6709;&#x4E00;&#x70B9;&#x70B9;&#x1F90F;&#x53D8;&#x66F4;&#xFF0C;&#x5728; docker &#x670D;&#x52A1;&#x91CC;&#x90FD;&#x7B97;&#x4F5C;&#x4E00;&#x6574;&#x4E2A;&#x6B65;&#x9AA4;&#x6709;&#x53D8;&#x66F4;&#xFF0C;&#x6240;&#x6709;&#x4F9D;&#x8D56;&#x90FD;&#x5F97;&#x8DDF;&#x7740;&#x4E00;&#x5757;&#x91CD;&#x65B0;&#x7F16;&#x8BD1;&#x3002;&#x4F46;&#x5B9E;&#x9645;&#x4E0A;&#x9879;&#x76EE;&#x4F9D;&#x8D56;&#x5F80;&#x5F80;&#x5F88;&#x5C11;&#x6709;&#x53D8;&#x66F4;&#xFF0C;&#x6839;&#x672C;&#x4E0D;&#x9700;&#x8981;&#x91CD;&#x65B0;&#x7F16;&#x8BD1;&#xFF0C;&#x90A3;&#x53EF;&#x4E0D;&#x53EF;&#x4EE5;&#x628A;&#x4E24;&#x79CD;&#x7F16;&#x8BD1;&#x62C6;&#x5206;&#x5F00;&#x5462;&#xFF1F;&#x770B;&#x4E0B;&#x9762;&#x4FEE;&#x6539;&#x7684; Dockerfile&#xFF1A;</p>
<pre><code class="language-dockerfile">FROM ... as builder
WORKDIR /workdir
RUN mkdir -p ./.cargo
COPY config.toml ./.cargo/

COPY Cargo.lock Cargo.toml ./
RUN mkdir -p src &amp;&amp; echo &apos;fn main() {}&apos; &gt; src/main.rs
RUN cargo update
RUN cargo build --release --bin dummy
RUN rm -Rvf src target/release/dummy

COPY ./ ./
RUN touch src/* &amp;&amp; cargo build --release --bin main
...
</code></pre>
<p>&#x89E3;&#x91CA;&#x4E0B;&#x5927;&#x610F;&#x5C31;&#x662F;&#x5148;&#x628A; cargo &#x9879;&#x76EE;&#x7684;&#x4F9D;&#x8D56;&#x6E05;&#x5355; <code>Cargo.toml</code>, <code>Cargo.lock</code> &#x62F7;&#x8FDB;&#x6765;&#xFF0C;&#x518D;&#x9020;&#x4E2A;&#x5047;&#x7684;&#x7A7A;&#x5165;&#x53E3;&#x51FD;&#x6570;&#xFF0C;&#x628A;&#x6240;&#x6709;&#x7684;&#x4F9D;&#x8D56;&#x5916;&#x52A0;&#x5047;&#x4E3B;&#x7A0B;&#x5E8F; <code>dummy</code> &#x7F16;&#x8BD1;&#x51FA;&#x6765;&#x3002;&#x5B8C;&#x4E8B;&#x540E;&#x5220;&#x9664;&#x5047;&#x7A0B;&#x5E8F;&#x5047;&#x6E90;&#x7801;&#xFF0C;&#x5C06;&#x771F;&#x9879;&#x76EE;&#x4EE3;&#x7801;&#x62F7;&#x5165;&#x518D; build&#xFF0C;&#x56E0;&#x4E3A;&#x4E0A;&#x4E00;&#x4E2A;&#x6B65;&#x9AA4;&#x91CC;&#x6240;&#x6709;&#x4F9D;&#x8D56;&#x90FD;&#x6709;&#x7F16;&#x8BD1;&#x4EA7;&#x7269;&#x4E86;&#xFF0C;&#x6240;&#x4EE5;&#x53EA;&#x7F16;&#x8BD1;&#x9879;&#x76EE;&#x672C;&#x4F53;&#x3002;&#x8FD9;&#x6837;&#x4E0B;&#x6B21;&#x9879;&#x76EE;&#x4EE3;&#x7801;&#x518D;&#x6709;&#x6539;&#x52A8;&#xFF0C;&#x7B2C; 10 &#x884C;&#x4EE5;&#x524D;&#x5305;&#x62EC;&#x7F16;&#x8BD1;&#x4F9D;&#x8D56;&#x7684;&#x6B65;&#x9AA4;&#x6CA1;&#x6709;&#x53D8;&#x52A8;&#x5C31;&#x5168;&#x8DF3;&#x8FC7;&#xFF0C;&#x53EA;&#x9700;&#x8981;&#x91CD;&#x65B0;&#x7F16;&#x8BD1;&#x9879;&#x76EE;&#x672C;&#x4F53;&#x3002;</p>
<p>&#x5B9E;&#x9645;&#x6548;&#x679C;&#x5F88;&#x6EE1;&#x610F;&#xFF0C;&#x539F;&#x672C;&#x9700;&#x8981;&#x8FD1;&#x4E00;&#x5C0F;&#x65F6;&#xFF0C;&#x73B0;&#x5728;&#x5341;&#x79D2;&#x4E0D;&#x5230;&#xFF0C;&#x6BD4;&#x53E6;&#x4E00;&#x4E2A; python &#x6253;&#x5305;&#x9879;&#x76EE;&#x90FD;&#x5FEB;&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Patch qBittorrent: Listening on port while using proxy]]></title><description><![CDATA[Recently, I came across a situation where I needed to do some seeding, but the network I was on required a proxy to gain access to the Internet. Naively, I set up the proxy settings in qbittorrent, quickly found out that it did not work at all. Now we need to patch...]]></description><link>https://xcel.me/qbittorrent-listening-on-port-while-using-proxy/</link><guid isPermaLink="false">63668d1fb9aecb71c91c1b43</guid><category><![CDATA[qBittorrent]]></category><category><![CDATA[Hack]]></category><category><![CDATA[Proxy]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Fri, 04 Nov 2022 23:52:00 GMT</pubDate><media:content url="https://xcel.me/content/images/2022/11/safar-safarov-MSN8TFhJ0is-unsplash.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://xcel.me/content/images/2022/11/safar-safarov-MSN8TFhJ0is-unsplash.jpg" alt="Patch qBittorrent: Listening on port while using proxy"><p>First things first, to achieve that, we need a proxy where we can set up port forwarding on it, then we modify and compile qbittorrent ourselves. A VPN can work, but that requires UPnP/PMP support of the VPN, which many lacks in. And also, use with caution, if you do not want to expose your IP address accidentally, always use a VPN.</p>
<p>Recently, I came across a situation where I need to do some seeding, but the network I was on requires a proxy to gain access to the Internet. Naively, I set up the proxy settings in qbittorrent, quickly found out that it did not work at all.</p>
<p>After some long nights searching and digging, it turned out that the library qbittorrent (along with several other FOSS torrenting software) depends on, <a href="https://github.com/arvidn/libtorrent">libtorrent</a>, on some recent versions, completely disables port listening while <code>Use proxy for peer connections</code> is enabled, to avoid network traffic leakage, rendering it impossible to do seeding at all while using a proxy. But the authors never make this clear, and I suspect that qbittorrent authors haven&apos;t even realized this behavior. The setting page on qbittorrent doesn&apos;t reflect the change in port listening section at all when you enable proxy for peers.</p>
<p>So here come the easier parts. We find the corresponding conditional code, modify, patch, then recompile libtorrent. No changes in qbittorrent are needed, only libtorrent. Check out my patch file <code>listen-when-proxy-on.patch</code> based on <code>v2.0.7</code> of libtorrent at end of this article.</p>
<p>Since I&apos;m on archlinux (and yes, BTW, I&apos;m using arch :D), patching software is relatively convenient. Go grab the <code>PKGBUILD</code> script from arch offical package page <a href="https://archlinux.org/packages/extra/x86_64/libtorrent-rasterbar/">https://archlinux.org/packages/extra/x86_64/libtorrent-rasterbar/</a> (source files link), add our patch into <code>prepare()</code> section like this:</p>
<pre><code class="language-bash">prepare() {
    makedir -p build
    cd &quot;$pkgname-$pkgver&quot;
    patch --forward --strip=1 --input=&quot;${srcdir}/listen-when-proxy-on.patch&quot;
}
</code></pre>
<p>and do <code>makepkg -Acs</code> to compile the package. After it&apos;s done, <code>sudo pacman -U libtorrent...</code> to do a force install. Then we need to do some config stuff.</p>
<ul>
<li>Fire up qbittorrent and set a fixed listening port, say <code>7890</code>;</li>
<li>Then set up port forwarding on the proxy server we&apos;re gonna use. I&apos;m using FRP for port forwarding on my proxy server, so I set up a tcp reverse tunnel with <code>local_port</code> as <code>7890</code> on my computer;</li>
<li>Go to qbittorrent advanced settings page, enable <code>Allow multiple connections from the same IP address</code>;</li>
<li>Finally, set proxy server for qbittorrent, and enable proxy for peer connections. Do not turn on UPnP/NAT-PMP. But if you know what you&apos;re doing, the patch ensures that they can be turned on along with proxy.</li>
</ul>
<p>Now we can add a torrent and see, it should start listening for incoming connections from the reverse tunnel we just set on the proxy server. And we can also see all the incoming peers shown as localhost or <code>127.0.0.1</code>. No worries, the bittorrent protocol doesn&apos;t care about peers&apos; ip addresses, from everyone else&apos;s perspective, you&apos;re just running qbittorrent on <code>7890</code> on the proxy server, everything works fine.</p>
<p>Disclaimer: qBittorrent and libtorrent are FOSS, the patch shared here is also free from restrictions. Please comply with your local regulations, use with caution and at your own risks.</p>
<p><code>listen-when-proxy-on.patch</code>:</p>
<pre><code class="language-patch">diff --git a/src/session_impl.cpp b/src/session_impl.cpp
index e25434926..8f38a3344 100644
--- a/src/session_impl.cpp
+++ b/src/session_impl.cpp
@@ -2016,7 +2016,7 @@ namespace {
 
 		// if we don&apos;t proxy peer connections, don&apos;t apply the special logic for
 		// proxies
-		if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none
+		if (false &amp;&amp; m_settings.get_int(settings_pack::proxy_type) != settings_pack::none
 			&amp;&amp; m_settings.get_bool(settings_pack::proxy_peer_connections))
 		{
 			// we will be able to accept incoming connections over UDP. so use
@@ -2774,7 +2774,7 @@ namespace {
 		// directly.
 		// This path is only for accepting incoming TCP sockets. The udp_socket
 		// class also restricts incoming packets based on proxy settings.
-		if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none
+		if (false &amp;&amp; m_settings.get_int(settings_pack::proxy_type) != settings_pack::none
 			&amp;&amp; m_settings.get_bool(settings_pack::proxy_peer_connections))
 			return;
 
@@ -5473,7 +5473,7 @@ namespace {
 			// connections. Not even uTP connections via the port we know about.
 			// The DHT may use the implied port to make it work, but the port we
 			// announce here has no relevance for that.
-			if (sock-&gt;flags &amp; listen_socket_t::proxy)
+			if (false &amp;&amp; sock-&gt;flags &amp; listen_socket_t::proxy)
 				return 0;
 
 			if (!(sock-&gt;flags &amp; listen_socket_t::accept_incoming))
@@ -5513,7 +5513,7 @@ namespace {
 			return std::uint16_t(sock-&gt;tcp_external_port());
 		}
 
-		if (m_settings.get_int(settings_pack::proxy_type) != settings_pack::none
+		if (false &amp;&amp; m_settings.get_int(settings_pack::proxy_type) != settings_pack::none
 			&amp;&amp; m_settings.get_bool(settings_pack::proxy_peer_connections))
 			return 0;
 
@@ -5611,7 +5611,7 @@ namespace {
 
 		if (!s-&gt;natpmp_mapper
 			&amp;&amp; !(s-&gt;flags &amp; listen_socket_t::local_network)
-			&amp;&amp; !(s-&gt;flags &amp; listen_socket_t::proxy))
+			&amp;&amp; !(s-&gt;flags &amp; listen_socket_t::proxy &amp;&amp; false))
 		{
 			// the natpmp constructor may fail and call the callbacks
 			// into the session_impl.
@@ -6833,8 +6833,9 @@ namespace {
 		// there&apos;s no point in starting the UPnP mapper for a network that isn&apos;t
 		// connected to the internet. The whole point is to forward ports through
 		// the gateway
+        // Enable UPnP anyway, even when on proxy, we can deal with this ourselves.
 		if ((s-&gt;flags &amp; listen_socket_t::local_network)
-			|| (s-&gt;flags &amp; listen_socket_t::proxy))
+			|| (s-&gt;flags &amp; listen_socket_t::proxy &amp;&amp; false))
 			return;
 
 		if (!s-&gt;upnp_mapper)
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[AMD CPU 做科学计算还香吗？（2022 年 MKL 与 OpenBLAS 的 NumPy 简单测试）]]></title><description><![CDATA[2022 年 MKL 与 OpenBLAS 在 NumPy 上的简单测试]]></description><link>https://xcel.me/amd-cpu-numpy-mkl-vs-openblas-2022/</link><guid isPermaLink="false">628f4f77b9aecb71c91c17d3</guid><category><![CDATA[Scientific Computing]]></category><category><![CDATA[Intel MKL]]></category><category><![CDATA[OpenBLAS]]></category><category><![CDATA[programming]]></category><category><![CDATA[Python]]></category><category><![CDATA[Hack]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Thu, 26 May 2022 13:30:31 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Zen &#x7CFB;&#x5217; CPU &#x8FD1;&#x4E94;&#x5E74;&#x591A;&#x6765;&#x53EF;&#x4EE5;&#x8BF4;&#x8D8A;&#x6765;&#x8D8A;&#x9999;&#xFF0C;&#x6E38;&#x620F;&#x3001;&#x76F4;&#x64AD;&#x3001;&#x89C6;&#x9891;&#x2026;&#x2026; &#x4E0D;&#x7BA1;&#x4EC0;&#x4E48;&#x5E73;&#x53F0;&#x4EC0;&#x4E48;&#x7528;&#x9014;&#xFF0C;&#x5728; Intel 12 &#x4EE3;&#x4EE5;&#x524D;&#x57FA;&#x672C;&#x90FD;&#x662F;&#x9996;&#x9009; AMD&#x3002;</p>
<p>&#x5148;&#x8BF4;&#x7ED3;&#x8BBA;&#xFF0C;AMD CPU &#x4E0A;&#x4E0D;&#x5C11;&#x8BA1;&#x7B97;&#x8FD8;&#x662F;&#x9700;&#x8981;&#x5F3A;&#x5236;&#x5F00; MKL &#x652F;&#x6301;&#x624D;&#x66F4;&#x9999;&#x3002;</p>
<h3 id="1-%E8%83%8C%E6%99%AF">1. &#x80CC;&#x666F;</h3>
<p>&#x6CA1;&#x63A5;&#x89E6;&#x8FC7;&#x7684;&#x53EF;&#x80FD;&#x5E76;&#x4E0D;&#x77E5;&#x9053;&#xFF0C;&#x79D1;&#x5B66;&#x8BA1;&#x7B97;&#x5176;&#x5B9E;&#x662F; AMD &#x5386;&#x6765;&#x6323;&#x624E;&#x7684;&#x4F20;&#x7EDF;&#x9879;&#x76EE;&#x3002;2018 &#x5E74;&#x5DE6;&#x53F3;&#x6709; MATLAB &#x7528;&#x6237;&#x53CD;&#x9988;&#x5728; Zen CPU &#x4E0A;&#x7684;&#x8FD0;&#x884C;&#x6548;&#x7387;&#x5947;&#x4F4E;&#xFF0C;&#x8FD8;&#x4E0D;&#x5982;&#x51E0;&#x5E74;&#x524D;&#x7684; Intel CPU&#x3002;&#x540E;&#x6765;&#x53C8;&#x6709;&#x4EBA;&#x6316;&#x51FA;&#x6765; MATLAB &#x7528;&#x7684;&#x7EBF;&#x4EE3;&#x52A0;&#x901F;&#x5E93;&#x662F; Intel &#x51FA;&#x7684; MKL&#xFF0C;&#x662F; Intel &#x6697;&#x4E2D;&#x7ED9;&#x7ADE;&#x4E89;&#x5BF9;&#x624B; AMD &#x4F7F;&#x7ECA;&#x5B50;&#xFF0C;&#x5728;&#x975E; Intel CPU &#x4E0A;&#x4E0D;&#x5F00;&#x542F; AVX2 &#x6307;&#x4EE4;&#x96C6;&#xFF0C;&#x964D;&#x4F4E;&#x7ADE;&#x4E89;&#x5BF9;&#x624B;&#x4EA7;&#x54C1;&#x7684;&#x6027;&#x80FD;&#x2026;&#x2026; &#x4E8E;&#x662F; Intel &#x4E00;&#x65F6;&#x906D;&#x5230;&#x5404;&#x8DEF;&#x4EBA;&#x58EB;&#x4E00;&#x81F4;&#x553E;&#x5F03;&#xFF0C;&#x4EE5;&#x81F3;&#x4E8E;&#x540E;&#x6765;&#x751A;&#x81F3;&#x6709;&#x6B63;&#x4E49;&#x4EBA;&#x58EB;&#x9006;&#x5411; MKL&#xFF0C;&#x6316;&#x51FA;&#x4E86;&#x90A3;&#x4E2A;&#x8457;&#x540D;&#x7684;&#x73AF;&#x5883;&#x53D8;&#x91CF; <code>MKL_DEBUG_CPU_TYPE=5</code>&#xFF0C;&#x5728; Zen CPU &#x4E0A;&#x5F3A;&#x5236;&#x542F;&#x7528; AVX2&#x3002;</p>
<p>&#x4E0D;&#x8FC7; Intel &#x8FD9;&#x505A;&#x6CD5;&#x5E76;&#x65E0;&#x95EE;&#x9898;&#xFF0C;MKL &#x672C;&#x8EAB;&#x4E0D;&#x662F;&#x5F00;&#x6E90;&#x8F6F;&#x4EF6;&#xFF0C;&#x53EA;&#x652F;&#x6301;&#x81EA;&#x5BB6;&#x4EA7;&#x54C1;&#x4E5F;&#x65E0;&#x53EF;&#x539A;&#x975E;&#x3002;&#x53CD;&#x5012;&#x662F; AMD &#x65E2;&#x8D5A;&#x5406;&#x559D;&#x53C8;&#x8D5A;&#x4E70;&#x5356;&#xFF0C;&#x5374;&#x8FDF;&#x8FDF;&#x6CA1;&#x6709;&#x5728;&#x8F6F;&#x4EF6;&#x652F;&#x6301;&#x4E0A;&#x8D76;&#x4E0A;&#x5BF9;&#x624B;&#xFF0C;&#x8FD8;&#x8981;&#x9760;&#x7528;&#x6237;&#x81EA;&#x5DF1;&#x53BB;&#x201C;&#x9ED1;&#x201D;&#x7740;&#x7528;&#x7ADE;&#x4E89;&#x5BF9;&#x624B;&#x7684; MKL&#xFF0C;&#x53EF;&#x4EE5;&#x8BF4;&#x5F88;&#x62C9;&#x57AE;&#x4E86;&#x3002;</p>
<p>&#x4E8B;&#x60C5;&#x5230;&#x8FD9;&#x672C;&#x8BE5;&#x7ED3;&#x675F;&#x4E86;&#xFF0C;&#x7136;&#x800C;&#x6CA1;&#x60F3;&#x5230; 2020 &#x5E74; Intel &#x5728;&#x65B0;&#x7248;&#x672C; MKL &#x91CC;&#x53C8;&#x6697;&#x6233;&#x6233;&#x5220;&#x6389;&#x4E86;&#x8FD9;&#x4E2A;&#x73AF;&#x5883;&#x53D8;&#x91CF;&#xFF0C;&#x641E;&#x5F97;&#x4E00;&#x6279; AMD &#x7528;&#x6237;&#x53C8;&#x662F;&#x5404;&#x79CD;&#x54C0;&#x568E;&#x5404;&#x79CD;&#x964D;&#x7EA7;&#x3002;&#x4E0D;&#x8FC7;&#x597D;&#x5728;&#x8FD9;&#x4E2A;&#x65F6;&#x5019;&#x5DF2;&#x7ECF;&#x6709;&#x53EF;&#x7528;&#x7684;&#x5F00;&#x6E90;&#x66FF;&#x4EE3;&#x54C1; OpenBLAS &#x4E86;&#xFF0C;&#x867D;&#x7136;&#x67D0;&#x4E9B;&#x6027;&#x80FD;&#x8FD8;&#x8D76;&#x4E0D;&#x4E0A; MKL&#xFF0C;&#x4F46;&#x662F;&#x4E5F;&#x6BD4;&#x6CA1;&#x6709;&#x52A0;&#x901F;&#x5F3A;&#x591A;&#x4E86;&#x3002;&#x5E76;&#x4E14;&#x4ECE;&#x8FD9;&#x4E2A;&#x65F6;&#x5019;&#x5F00;&#x59CB;&#xFF0C;NumPy &#x7B49;&#x4E00;&#x7968;&#x8BA1;&#x7B97;&#x5E93;&#x4E5F;&#x6E10;&#x6E10;&#x5411;&#x5F00;&#x6E90;&#x7684; OpenBLAS &#x9760;&#x62E2;&#x3002;</p>
<p>&#x4E0D;&#x8FC7;&#x540C;&#x5E74;&#x4E5F;&#x6709;&#x4EBA;&#x53D1;&#x73B0; Intel &#x79FB;&#x9664;&#x4E86; MKL &#x91CC;&#x90A3;&#x4E2A;&#x53D8;&#x91CF;&#x7684;&#x540C;&#x65F6;&#xFF0C;&#x770B;&#x8D77;&#x6765;&#x8FD8;&#x5BF9; Zen CPU &#x65B0;&#x589E;&#x4E86;&#x67D0;&#x4E9B;&#x8FD0;&#x7B97;&#x7684;&#x52A0;&#x901F;&#x652F;&#x6301;&#xFF0C;&#x4E5F;&#x8BB8;&#x662F;&#x51FA;&#x4E8E;&#x53CD;&#x5784;&#x65AD;&#x5B98;&#x53F8;&#x7684;&#x8003;&#x8651;&#x3002;&#x540C;&#x65F6;&#x8FD8;&#x6709;&#x4EBA;&#x8FDB;&#x4E00;&#x6B65;&#x9006;&#x5411; MKL&#xFF0C;&#x8FD8;&#x80FD;&#x7528;&#x52A8;&#x6001;&#x5E93;&#x52AB;&#x6301;&#x7684;&#x65B9;&#x6CD5;&#x5F3A;&#x5236;&#x5F00;&#x542F;&#x5BF9; Zen AVX/AVX2 &#x6307;&#x4EE4;&#x96C6;&#x7684;&#x5B8C;&#x6574;&#x652F;&#x6301;&#x3002;&#x8FD9;&#x4E2A;&#x5728;&#x4E2D;&#x6587;&#x5708;&#x57FA;&#x672C;&#x4E0A;&#x6CA1;&#x4EC0;&#x4E48;&#x4EBA;&#x63D0;&#x5230;&#xFF0C;<a href="https://danieldk.eu/Posts/2020-08-31-MKL-Zen.html">&#x539F;&#x6587;&#x5728;&#x6B64;&#x67E5;&#x770B;</a>&#x3002;</p>
<p>Linux &#x4E0A;&#x57FA;&#x672C;&#x662F;&#x8FD9;&#x6837;&#x64CD;&#x4F5C;&#x7684;&#xFF0C;&#x5148;&#x5199;&#x4E2A;&#x51FD;&#x6570; <code>fakeintel.c</code></p>
<pre><code class="language-c">int mkl_serv_intel_cpu_true() { return 1; }
</code></pre>
<p>&#x7F16;&#x8BD1;&#x4E00;&#x4E0B;</p>
<pre><code class="language-bash">gcc -shared -fPIC -o libfakeintel.so fakeintel.c
</code></pre>
<p>&#x7136;&#x540E;&#x4F7F;&#x7528;&#x7684;&#x65F6;&#x5019;&#x5E26;&#x4E0A; <code>LD_PRELOAD</code> &#x53BB;&#x52AB;&#x6301;&#x7A0B;&#x5E8F;&#xFF0C;&#x5C31;&#x80FD;&#x4F7F;&#x7528; MKL &#x7684;&#x52A0;&#x901F;&#x4E86;&#x3002;</p>
<pre><code class="language-bash">LD_PRELOAD=./libfakeintel.so python test.py
</code></pre>
<h3 id="2-%E5%AE%89%E8%A3%85%E7%8E%AF%E5%A2%83">2. &#x5B89;&#x88C5;&#x73AF;&#x5883;</h3>
<p>&#x9996;&#x5148;&#x8BF4;&#x4E0B;&#x6211;&#x7684;&#x662F; 2700x &#x7684; AMD Zen+ CPU&#xFF0C;DDR4 64GB@2933MHz &#x7684;&#x53F0;&#x5F0F;&#x3002;</p>
<p>Python &#x73AF;&#x5883;&#x4E2D;&#x8981;&#x67E5;&#x770B; blas &#x5E93;&#x4FE1;&#x606F;&#xFF0C;&#x4E00;&#x822C;&#x662F;&#x7528; <code>numpy</code> &#x5E26;&#x7684;&#x90A3;&#x4E2A;&#x6253;&#x5370; blas &#x4FE1;&#x606F;&#x7684;&#x51FD;&#x6570; <code>np.show_config</code>&#xFF0C;&#x4E0D;&#x8FC7;&#x5B9E;&#x6D4B;&#x53EF;&#x80FD;&#x5E76;&#x4E0D;&#x51C6;&#x786E;&#x3002;&#x4E0B;&#x9762;&#x8BB2;&#x4E0B;&#x5982;&#x4F55;&#x6B63;&#x786E;&#x5B89;&#x88C5;&#x94FE;&#x63A5;&#x4E86; mkl &#x7684; numpy&#x3002;</p>
<p>&#x4F7F;&#x7528; conda &#x6BD4; pip &#x65B9;&#x4FBF;&#x4E0D;&#x5C11;&#xFF0C;&#x5F00;&#x542F; conda-forge channel &#x8FD8;&#x80FD;&#x6709;&#x66F4;&#x591A;&#x8F6F;&#x4EF6;&#x5305;&#x7684;&#x4E0D;&#x540C; build &#x53EF;&#x4F9B;&#x9009;&#x62E9;&#xFF0C;&#x8FD9;&#x91CC;&#x7528; conda &#x4E3E;&#x4F8B;&#x3002;&#x5148;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x65B0;&#x7684;&#x73AF;&#x5883;&#x53EB; <code>numpy_openblas</code></p>
<pre><code class="language-bash">conda create python numpy &quot;libblas=*=*openblas&quot; &quot;blas=*=*openblas&quot; -n numpy_openblas
</code></pre>
<p>&#x542F;&#x7528;&#x65B0;&#x73AF;&#x5883;&#xFF0C;&#x518D;&#x9A8C;&#x8BC1;&#x4E0B; blas</p>
<pre><code class="language-bash">conda activate numpy_openblas
conda list | grep blas
</code></pre>
<p>&#x53EF;&#x4EE5;&#x770B;&#x5230;&#x8F93;&#x51FA;&#x8F6F;&#x4EF6;&#x5305;&#xFF0C;&#x5DF2;&#x7ECF;&#x5B89;&#x88C5;&#x4F7F;&#x7528;&#x4E86; openblas<br>
<img src="https://xcel.me/content/images/2022/05/conda_list_openblas.png" alt="conda_list_openblas" loading="lazy"></p>
<p>&#x4F46;&#x7528; <code>np.show_config()</code> &#x6253;&#x5370;&#x51FA;&#x7684;&#x4FE1;&#x606F;&#x5E76;&#x4E0D;&#x51C6;&#x786E;&#xFF1A;</p>
<pre><code class="language-python">blas_mkl_info:
    libraries = [&apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;, &apos;pthread&apos;, &apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;]
    library_dirs = [&apos;/home/cabbage/.conda/envs/numpy_openblas/lib&apos;]
    define_macros = [(&apos;SCIPY_MKL_H&apos;, None), (&apos;HAVE_CBLAS&apos;, None)]
    include_dirs = [&apos;/home/cabbage/.conda/envs/numpy_openblas/include&apos;]
blas_opt_info:
    libraries = [&apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;, &apos;pthread&apos;, &apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;, &apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;]
    library_dirs = [&apos;/home/cabbage/.conda/envs/numpy_openblas/lib&apos;]
    define_macros = [(&apos;SCIPY_MKL_H&apos;, None), (&apos;HAVE_CBLAS&apos;, None)]
    include_dirs = [&apos;/home/cabbage/.conda/envs/numpy_openblas/include&apos;]
lapack_mkl_info:
    libraries = [&apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;, &apos;pthread&apos;, &apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;]
    library_dirs = [&apos;/home/cabbage/.conda/envs/numpy_openblas/lib&apos;]
    define_macros = [(&apos;SCIPY_MKL_H&apos;, None), (&apos;HAVE_CBLAS&apos;, None)]
    include_dirs = [&apos;/home/cabbage/.conda/envs/numpy_openblas/include&apos;]
lapack_opt_info:
    libraries = [&apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;, &apos;pthread&apos;, &apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;, &apos;blas&apos;, &apos;cblas&apos;, &apos;lapack&apos;]
    library_dirs = [&apos;/home/cabbage/.conda/envs/numpy_openblas/lib&apos;]
    define_macros = [(&apos;SCIPY_MKL_H&apos;, None), (&apos;HAVE_CBLAS&apos;, None)]
    include_dirs = [&apos;/home/cabbage/.conda/envs/numpy_openblas/include&apos;]
</code></pre>
<p>&#x6316;&#x4E86;&#x4E0B;&#x53D1;&#x73B0; conda &#x8FD9;&#x91CC;&#x5B89;&#x88C5;&#x5B8C; openblas &#x662F;&#x8F6F;&#x94FE;&#x5230;&#x4E86; cblas &#x4E0A;&#xFF0C;&#x731C;&#x6D4B;&#x548C;&#x8FD9;&#x4E2A;&#x6709;&#x5173;&#x7CFB;&#x3002;&#x4F46;&#x662F;&#x4E0D;&#x7BA1;&#x600E;&#x4E48;&#x6837;&#xFF0C;&#x540E;&#x9762;&#x8DD1;&#x6D4B;&#x8BD5;&#x5BF9;&#x6BD4;&#x4E0B;&#x6D4B;&#x8BD5;&#x5C31;&#x90FD;&#x6E05;&#x695A;&#x4E86;&#x3002;</p>
<p>&#x63A5;&#x7740;&#x53E6;&#x5916;&#x518D;&#x5EFA;&#x4E2A;&#x73AF;&#x5883;&#xFF0C;&#x5B89;&#x88C5; mkl &#x7684; numpy&#xFF1A;</p>
<pre><code class="language-bash">conda deactivate
conda create python numpy &quot;libblas=*=*mkl&quot; &quot;blas=*=*mkl&quot; -n numpy_mkl
conda activate numpy_mkl
</code></pre>
<p>&#x68C0;&#x67E5;&#x4E0B; <code>conda list | grep blas</code> &#x5E94;&#x8BE5;&#x662F; mkl &#x4E86;&#xFF1A;<br>
<img src="https://xcel.me/content/images/2022/05/conda_list_mkl.png" alt="conda_list_mkl" loading="lazy"></p>
<h3 id="3-%E6%B5%8B%E8%AF%95%E7%94%A8%E7%9A%84%E4%BB%A3%E7%A0%81">3. &#x6D4B;&#x8BD5;&#x7528;&#x7684;&#x4EE3;&#x7801;</h3>
<pre><code class="language-python">import numpy as np
from time import time
#print(&apos;Numpy config:&apos;)
#np.show_config()

# &#x751F;&#x6210;&#x6D4B;&#x8BD5;&#x6570;&#x636E;&#xFF0C;&#x63A7;&#x5236;&#x53D8;&#x91CF;&#xFF0C;&#x975E;&#x968F;&#x673A;
np.random.seed(0)
size = 4096
A, B = np.random.random((size, size)), np.random.random((size, size))
C, D = np.random.random((size * 128,)), np.random.random((size * 128,))
E = np.random.random((size // 2, size // 4))
F = G = np.random.random((size // 2, size // 2))
F = np.dot(F, F.T)

# &#x77E9;&#x9635;&#x70B9;&#x4E58;
def dot(N=20):
    t = time()
    for i in range(N):
        np.dot(A, B)
    print(f&apos;&#x70B9;&#x4E58;&#x4E24;&#x4E2A; {size}x{size} &#x77E9;&#x9635; {N} &#x6B21;&#xFF0C;&#x5E73;&#x5747;&#x7528;&#x65F6; {(time() - t) / N} &#x79D2;&apos;)
    
# &#x5411;&#x91CF;&#x4E58;&#x6CD5;
def v_dot(N=5000):
    t = time()
    for i in range(N):
        np.dot(C, D)
    print(f&apos;&#x70B9;&#x4E58;&#x4E24;&#x4E2A; {size * 128} &#x5411;&#x91CF; {N} &#x6B21;&#xFF0C;&#x5E73;&#x5747;&#x7528;&#x65F6; {(time() - t) / N * 1000} &#x6BEB;&#x79D2;&apos;)
    
# &#x5947;&#x5F02;&#x503C;&#x5206;&#x89E3;
def svd(N=3):
    t = time()
    for i in range(N):
        np.linalg.svd(E, full_matrices=False)
    print(f&apos;&#x5206;&#x89E3;&#x4E00;&#x4E2A; {size // 2}x{size // 4} &#x77E9;&#x9635;&#x7684;&#x5947;&#x5F02;&#x503C; {N} &#x6B21;&#xFF0C;&#x5E73;&#x5747;&#x7528;&#x65F6; {(time() - t) / N} &#x79D2;&apos;)

# &#x79D1;&#x5217;&#x65AF;&#x57FA;&#x5206;&#x89E3;
def cholesky(N=3):
    t = time()
    for i in range(N):
        np.linalg.cholesky(F)
    print(f&apos;&#x5BF9;&#x4E00;&#x4E2A; {size // 2}x{size // 2} &#x77E9;&#x9635;&#x79D1;&#x5217;&#x65AF;&#x57FA;&#x5206;&#x89E3; {N} &#x6B21;&#xFF0C;&#x5E73;&#x5747;&#x7528;&#x65F6; {(time() - t) / N} &#x79D2;&apos;)
    
# &#x7279;&#x5F81;&#x5206;&#x89E3;
def eig(N=3):
    t = time()
    for i in range(N):
        np.linalg.eig(G)
    print(f&apos;&#x5BF9;&#x4E00;&#x4E2A; {size // 2}x{size // 2} &#x77E9;&#x9635;&#x5206;&#x89E3;&#x7279;&#x5F81;&#x503C; {N} &#x6B21;&#xFF0C;&#x5E73;&#x5747;&#x7528;&#x65F6; {(time() - t) / N} &#x79D2;&apos;)

if __name__ == &apos;__main__&apos;:
    dot()
    v_dot()
    svd()
    cholesky()
    eig()
</code></pre>
<h3 id="4-%E8%BF%90%E8%A1%8C%E5%90%84-blas-%E5%BA%93%E7%9A%84-numpy-%E6%B5%8B%E8%AF%95">4. &#x8FD0;&#x884C;&#x5404; blas &#x5E93;&#x7684; numpy &#x6D4B;&#x8BD5;</h3>
<p>&#x6211;&#x7684;&#x73AF;&#x5883;&#x662F; 2700x &#x7684; AMD Zen+ CPU&#xFF0C;64GB@2933MHz DDR4 &#x7684;&#x53F0;&#x5F0F;&#x3002;</p>
<ul>
<li>
<p>&#x4F7F;&#x7528; OpenBLAS &#x7684; numpy</p>
<pre><code class="language-bash">conda activate numpy_openblas
python test.py
</code></pre>
<p><img src="https://xcel.me/content/images/2022/05/openblas_test.png" alt="openblas_test" loading="lazy"></p>
</li>
<li>
<p>mkl &#x7684; numpy&#xFF0C;&#x4E0D;&#x5F3A;&#x5236;&#x5F00;&#x542F;&#x5B8C;&#x6574;&#x652F;&#x6301;</p>
<pre><code class="language-bash">conda activate numpy_mkl
python test.py
</code></pre>
<p><img src="https://xcel.me/content/images/2022/05/mkl_test.png" alt="mkl_test" loading="lazy"></p>
</li>
<li>
<p>mkl &#x7684; numpy&#xFF0C;&#x7528;&#x4E0A;&#x8FB9;&#x63D0;&#x5230;&#x52A8;&#x6001;&#x5E93;&#x52AB;&#x6301;&#x7684;&#x65B9;&#x6CD5;&#x5F3A;&#x5236;&#x5F00;&#x542F;&#x5B8C;&#x6574;&#x652F;&#x6301;</p>
<pre><code class="language-bash">conda activate numpy_mkl
LD_PRELOAD=./libfakeintel.so python test.py
</code></pre>
<p><img src="https://xcel.me/content/images/2022/05/mkl_force_test.png" alt="mkl_force_test" loading="lazy"></p>
</li>
</ul>
<h3 id="5-%E7%BB%93%E8%AE%BA">5. &#x7ED3;&#x8BBA;</h3>
<p>&#x6839;&#x636E;&#x8FD9;&#x91CC;&#x7684;&#x6D4B;&#x8BD5;&#x6765;&#x770B;&#xFF0C;Intel MKL &#x4ECD;&#x7136;&#x5BF9; AMD CPU &#x6709;&#x4E0D;&#x5C11;&#x52A3;&#x5316;&#xFF0C;&#x5E76;&#x4E14;&#x5F3A;&#x5F00;&#x7684; MKL &#x6027;&#x80FD;&#x4ECD;&#x7136;&#x6BD4; OpenBLAS &#x597D;&#x4E0D;&#x5C11;&#x3002;&#x81F3;&#x5C11;&#x5728;&#x8FD9;&#x91CC; Zen+ &#x7684; 2700x CPU &#x4E0A;&#xFF0C;&#x8FD9;&#x4E2A;&#x7ED3;&#x8BBA;&#x662F;&#x5F88;&#x660E;&#x663E;&#x7684;&#x4E86;&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[PySpark DataFrame 写入 Elasticsearch]]></title><description><![CDATA[在 spark 中使用 elasticsearch-hadoop 将 dataframe 写入 elasticsearch 服务]]></description><link>https://xcel.me/pyspark-write-to-elasticsearch/</link><guid isPermaLink="false">61dc55bcdc7adc32ced0d366</guid><category><![CDATA[Spark]]></category><category><![CDATA[Python]]></category><category><![CDATA[Elasticsearch]]></category><category><![CDATA[programming]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Sat, 29 Jan 2022 08:55:36 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>&#x9996;&#x5148;&#x60F3;&#x5230;&#x7684;&#x662F;&#x901A;&#x8FC7; JDBC &#x52A0;&#x9A71;&#x52A8;&#x7684;&#x65B9;&#x5F0F;&#x5199;&#x5165; ES&#xFF0C;&#x56E0;&#x4E3A; ES &#x672C;&#x8EAB;&#x6709;&#x63D0;&#x4F9B; JDBC &#x9A71;&#x52A8;&#x3002;&#x7136;&#x800C;&#x5B9E;&#x9645;&#x5C1D;&#x8BD5;&#x540E;&#x53D1;&#x73B0; ES SQL &#x5E76;&#x4E0D;&#x652F;&#x6301;&#x66F4;&#x6539;&#x6216;&#x8005;&#x5199;&#x5165;&#xFF0C;&#x53EA;&#x652F;&#x6301;&#x6709;&#x9650;&#x7684;&#x67E5;&#x8BE2;&#x64CD;&#x4F5C;&#x3002;&#x4E0D;&#x8FC7; ES &#x5DF2;&#x7ECF;&#x901A;&#x8FC7; Hadoop &#x539F;&#x751F;&#x652F;&#x6301;&#x4E86; Spark&#xFF0C;&#x5373; <code>elasticsearch-hadoop</code> &#x5E93;&#x3002;<a href="https://www.elastic.co/guide/en/elasticsearch/hadoop/current/spark.html">&#x5B98;&#x65B9;&#x6587;&#x6863;</a>&#x63D0;&#x4F9B;&#x4E86; scala &#x7684;&#x8BE6;&#x7EC6;&#x4F7F;&#x7528;&#x8BF4;&#x660E;&#xFF0C;&#x5E76;&#x6CA1;&#x6709;&#x8BE6;&#x7EC6;&#x8BF4;&#x660E; pyspark &#x7684;&#x4F7F;&#x7528;&#xFF0C;&#x4F46;&#x901A;&#x8FC7;&#x62FC;&#x63A5; java class &#x7684;&#x914D;&#x7F6E;&#x53C2;&#x6570;&#xFF0C;pyspark &#x5176;&#x5B9E;&#x4E5F;&#x80FD;&#x4E00;&#x6837;&#x8C03;&#x7528; <code>elasticsearch-hadoop</code>&#x3002;</p>
<p>&#x9996;&#x5148;&#x53BB;&#x4E0B;&#x8F7D; jar &#x5305;&#xFF0C;<a href="https://elastic.co/downloads/hadoop">&#x53EF;&#x4EE5;&#x70B9;&#x8FD9;&#x91CC;&#x627E;&#x5230;&#x6700;&#x8FD1;&#x7248;&#x672C;&#x624B;&#x52A8;&#x4E0B;&#x8F7D;</a>&#xFF0C;&#x6216;&#x8005;&#x6DFB;&#x52A0; maven &#x914D;&#x7F6E;</p>
<pre><code class="language-xml">&lt;dependency&gt;
  &lt;groupId&gt;org.elasticsearch&lt;/groupId&gt;
  &lt;artifactId&gt;elasticsearch-spark-30_2.12&lt;/artifactId&gt; 
  &lt;version&gt;7.16.3&lt;/version&gt;
&lt;/dependency&gt;
</code></pre>
<p>&#x63A5;&#x4E0B;&#x6765;&#x5728; pyspark &#x542F;&#x52A8;&#x53C2;&#x6570; <code>--jars</code> &#x4E2D;&#x52A0;&#x5165; <code>elasticsearch-hadoop-xxx.jar</code> &#x6216;&#x8005;&#x628A; jar &#x5305;&#x653E;&#x5165; classpath&#xFF0C;&#x8FD9;&#x6837;&#x5C31;&#x8F7D;&#x5165;&#x4E86;&#x5BF9;&#x5E94; class&#x3002;</p>
<p>spark &#x7531;&#x4E8E;&#x7248;&#x672C;&#x539F;&#x56E0;&#xFF0C;2.x &#x4E2D;&#x53EF;&#x7528;&#x7684;&#x591A;&#x5408;&#x4E00;&#x652F;&#x6301;&#x5927;&#x5305; <code>elasticsearch-hadoop-7.16.2.jar</code> &#x5230; 3.x &#x53CA;&#x4EE5;&#x4E0A;&#x5C31;&#x4E0D;&#x517C;&#x5BB9;&#x4E86;&#xFF0C;&#x9700;&#x8981;&#x4F7F;&#x7528;&#x5355;&#x72EC;&#x652F;&#x6301; spark &#x7684; <code>elasticsearch-spark-30.2.12.jar</code>&#x3002;</p>
<p>&#x542F;&#x52A8; pyspark&#xFF0C;&#x5047;&#x8BBE;&#x6709;&#x4E00;&#x4E2A; DataFrame <code>df</code> &#x9700;&#x5199;&#x5165;&#x672C;&#x5730;&#x4E00;&#x4E2A; elasticsearch &#x670D;&#x52A1;&#x3002;&#x4E0B;&#x8FB9;&#x7528;&#x4E00;&#x4E2A; notebook &#x793A;&#x4F8B;&#x3002;</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><iframe src="https://xcel.me/content/files/2022/01/export.html" id="iFrame1" onload="javascript:(function(o){o.style.height=o.contentWindow.document.body.scrollHeight+&quot;px&quot;;}(this));" style="height:200px;width:100%;border:none;overflow:hidden;"></iframe><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>&#x9996;&#x5148;&#xFF0C;elasticsearch &#x539F;&#x751F;&#x4E0D;&#x652F;&#x6301; spark &#x4E2D;&#x7684; <code>DecimalType</code> &#x6570;&#x636E;&#x7C7B;&#x578B;&#xFF0C;&#x6240;&#x4EE5;&#x9700;&#x8981;&#x628A; <code>DecimalType</code> &#x7C7B;&#x578B;&#x7684;&#x5217;&#x90FD;&#x8F6C;&#x6362;&#x6210;&#x6D6E;&#x70B9;&#x578B; <code>DoubleType</code>&#xFF0C;&#x5982;&#x4E0A;&#x6587;&#x51FD;&#x6570; <code>_cast_decimal_to_double</code> &#x6240;&#x793A;&#x3002;</p>
<p>&#x5728;&#x4E0A;&#x8FB9;&#x7684;&#x4F8B;&#x5B50;&#x4E2D;&#xFF0C;&#x6700;&#x540E;&#x5199;&#x5165; dataframe &#x65F6;&#x5404;&#x9009;&#x9879;&#x542B;&#x4E49;&#xFF1A;</p>
<ul>
<li><strong><code>.format(&apos;es&apos;)</code></strong> &#x4F7F;&#x7528; elasticsearch-hadoop &#x5199;&#x5165; rdd &#x5230; elasticsearch &#x670D;&#x52A1;&#x4E2D;&#x3002;&#x5B9E;&#x9645;&#x4E0A;&#x8FD9;&#x91CC; <code>&apos;es&apos;</code> &#x53EA;&#x662F; <code>&apos;org.elasticsearch.spark.sql&apos;</code> &#x6CE8;&#x518C;&#x7684;&#x522B;&#x540D;&#xFF0C;&#x5B98;&#x65B9;&#x6587;&#x6863;&#x4E2D;&#x5199;&#x5168;&#x4E86;&#x662F; <code>.format(&apos;org.elasticsearch.spark.sql&apos;)</code>&#xFF1B;</li>
<li><strong><code>.mode(&apos;overwrite&apos;)</code></strong> &#x91C7;&#x7528; spark &#x8986;&#x76D6;&#x5199;&#x5165;&#x7684;&#x65B9;&#x5F0F;&#xFF0C;&#x5728; elasticsearch-hadoop &#x4E2D;&#x4F53;&#x73B0;&#x7684;&#x884C;&#x4E3A;&#x5C31;&#x662F;&#x6BCF;&#x6B21;&#x5199;&#x5165;&#x4F1A;&#x5148;&#x6E05;&#x7A7A;&#x6307;&#x5B9A;&#x7684; index&#xFF0C;&#x7136;&#x540E;&#x518D;&#x5199;&#x5165;&#x65B0;&#x7684;&#x8BB0;&#x5F55;&#xFF1B;</li>
<li><strong><code>&apos;es.resource&apos;: &apos;test_index&apos;</code></strong>&#x3002;&#x8FD9;&#x91CC;&#x6307;&#x5B9A; <code>df</code> &#x8981;&#x88AB;&#x5199;&#x5165;&#x5230;&#x540D;&#x4E3A; <code>test_index</code> &#x7684; index &#x4E2D;&#x3002;&#x6B64;&#x5904;&#x4E5F;&#x53EF;&#x6307;&#x5B9A;&#x6587;&#x6863;&#x7C7B;&#x578B;&#xFF0C;&#x5982; <code>&apos;test_index/titanic&apos;</code> &#x5C31;&#x6307;&#x5B9A;&#x5199;&#x5165;&#x5230; <code>test_index</code> &#x4E2D;&#xFF0C;&#x5E76;&#x4E14;&#x6587;&#x6863;&#x7C7B;&#x578B;&#x4E3A; <code>titanic</code>&#xFF1B;</li>
<li><strong><code>&apos;es.nodes.wan.only&apos;: &apos;true&apos;</code></strong>&#x3002;&#x8FD9;&#x91CC;&#x8868;&#x793A;&#x8FDE;&#x63A5;&#x5230; elasticsearch &#x96C6;&#x7FA4;&#x65F6;&#x7981;&#x7528;&#x5730;&#x5740;&#x55C5;&#x63A2;&#xFF0C;&#x53EA;&#x901A;&#x8FC7;&#x914D;&#x7F6E;&#x9879; <code>es.nodes</code> &#x63D0;&#x4F9B;&#x7684;&#x5730;&#x5740;&#x53BB;&#x8FDE;&#x63A5;&#x3002;&#x6CE8;&#x610F;&#xFF0C;&#x5982;&#x679C;&#x8981;&#x8FDE;&#x63A5;&#x5728;&#x5185;&#x7F51;&#x73AF;&#x5883;&#x4E2D;&#x90E8;&#x7F72;&#x7684; elasticsearch &#x800C;&#x4E0D;&#x6253;&#x5F00;&#x672C;&#x6761;&#x8BBE;&#x7F6E;&#xFF0C;spark &#x5BA2;&#x6237;&#x7AEF;&#x5F88;&#x53EF;&#x80FD;&#x4F1A;&#x55C5;&#x63A2;&#x5230;&#x9519;&#x8BEF;&#x7684;&#x5185;&#x7F51;&#x5730;&#x5740;&#xFF0C;&#x4ECE;&#x800C;&#x4E00;&#x76F4;&#x5361;&#x4F4F;&#x800C;&#x65E0;&#x6CD5;&#x8FDE;&#x63A5;&#x3002;&#x5173;&#x4E8E;&#x5730;&#x5740;&#x55C5;&#x63A2;&#x548C;&#x5176;&#x4ED6;&#x6709;&#x5173;&#x7684;&#x8BE6;&#x7EC6;&#x5185;&#x5BB9;<a href="https://www.elastic.co/guide/en/elasticsearch/hadoop/current/configuration.html">&#x53C2;&#x770B;&#x5B98;&#x65B9;&#x6587;&#x6863;</a>&#xFF1B;</li>
<li><strong><code>es.nodes</code>&#x3001;<code>es.port</code></strong> &#x5206;&#x522B;&#x6307;&#x5B9A;&#x8981;&#x8FDE;&#x63A5; elasticsearch &#x670D;&#x52A1;&#x7684;&#x5730;&#x5740;&#x7AEF;&#x53E3;&#xFF0C;&#x5176;&#x4E2D; <code>es.nodes</code> &#x53EF;&#x4EE5;&#x6307;&#x5B9A;&#x4E00;&#x4E2A;&#x96C6;&#x7FA4;&#x7684;&#x591A;&#x4E2A;&#x5730;&#x5740;&#xFF1B;</li>
<li><strong><code>es.net.http.auth.user</code>&#x3001;<code>es.net.http.auth.pass</code></strong> &#x6307;&#x5B9A;&#x7528;&#x6237;&#x540D;&#x5BC6;&#x7801;&#xFF1B;</li>
</ul>
<p>&#x5B9E;&#x9645;&#x4E0A;&#x9664;&#x4E86; spark &#x4E2D;&#x5B9A;&#x4E49;&#x7684; <code>.mode(&apos;overwrite&apos;)</code>&#x3001;<code>.mode(&apos;append&apos;)</code>&#xFF0C;elasticsearch-hadoop &#x672C;&#x8EAB;&#x4E5F;&#x5B9A;&#x4E49;&#x56DB;&#x79CD;&#x4E0D;&#x540C;&#x7684;&#x5199;&#x5165;&#x884C;&#x4E3A;&#xFF08;<a href="https://www.elastic.co/guide/en/elasticsearch/hadoop/current/configuration.html">&#x53C2;&#x89C1;&#x6587;&#x6863;&#x4E2D;&#x9009;&#x9879; <code>es.write.operation</code></a>&#xFF09;&#xFF1A;</p>
<ul>
<li><strong><code>index</code></strong>&#xFF0C;&#x8FD9;&#x662F;&#x9ED8;&#x8BA4;&#x884C;&#x4E3A;&#xFF0C;&#x65B0;&#x589E;&#x6587;&#x6863;&#x8BB0;&#x5F55;&#xFF0C;&#x5E76;&#x7531; elasticsearch &#x751F;&#x6210;&#x6BCF;&#x6761;&#x6570;&#x636E;&#x7684; id&#xFF0C;&#x91CD;&#x590D; id &#x7684;&#x8001;&#x6570;&#x636E;&#x4F1A;&#x88AB;&#x66FF;&#x6362;&#xFF1B;</li>
<li><strong><code>create</code></strong>&#xFF0C;&#x540C;&#x6837;&#x662F;&#x65B0;&#x589E;&#x6587;&#x6863;&#x8BB0;&#x5F55;&#xFF0C;&#x4F46;&#x9047;&#x5230;&#x91CD;&#x590D; id &#x7684;&#x8001;&#x6570;&#x636E;&#x4F1A;&#x629B;&#x5F02;&#x5E38;&#xFF1B;</li>
<li><strong><code>update</code></strong>&#xFF0C;&#x6839;&#x636E; id &#x66F4;&#x65B0;&#x5BF9;&#x5E94;&#x6587;&#x6863;&#xFF1B;</li>
<li><strong><code>upsert</code></strong>&#xFF0C;&#x6839;&#x636E; id &#x5408;&#x5E76;&#x591A;&#x6761;&#x8BB0;&#x5F55;&#xFF0C;&#x5373;&#x65B0;&#x589E;&#x4E0D;&#x5B58;&#x5728;&#x7684;&#x8BB0;&#x5F55;&#xFF0C;&#x5E76;&#x66F4;&#x65B0;&#x5DF2;&#x6709;&#x7684;&#x8BB0;&#x5F55;&#x3002;&#x6CE8;&#x610F;&#x8FD9;&#x79CD;&#x65B9;&#x5F0F;&#x8FD8;&#x9700;&#x914D;&#x5408;&#x4F7F;&#x7528; spark &#x7684;&#x5199;&#x5165;&#x6A21;&#x5F0F; <code>.mode(&apos;append&apos;)</code>&#xFF0C;&#x540C;&#x65F6;&#x8FD8;&#x5FC5;&#x987B;&#x6307;&#x5B9A; dataframe &#x4E2D;&#x54EA;&#x4E00;&#x5217;&#x8981;&#x4F5C;&#x4E3A; id &#x53BB;&#x66F4;&#x65B0;&#x65E7;&#x7684;&#x8BB0;&#x5F55;&#xFF08;&#x9009;&#x9879; <code>es.mapping.id</code>&#xFF09;&#x3002;&#x6BD4;&#x5982; <code>.option(&apos;es.mapping.id&apos;, &apos;UserID&apos;)</code> &#x8FD9;&#x91CC;&#x6307;&#x5B9A; <code>UserID</code> &#x5217;&#x4F5C;&#x4E3A;&#x5173;&#x952E;&#x5B57;&#x6BB5;&#x53BB;&#x66F4;&#x65B0; es &#x5BF9;&#x5E94; index &#x4E2D;&#x5DF2;&#x6709;&#x7684;&#x6587;&#x6863;&#x8BB0;&#x5F55;&#xFF1B;</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[OpenSSH 动态增减隧道转发]]></title><description><![CDATA[几个你可能不知道的 OpenSSH 小技巧，比如在已有连接上你可以随时开关新的隧道，不必另开新连接。]]></description><link>https://xcel.me/openssh-tilde-escaping/</link><guid isPermaLink="false">6198dd83dc7adc32ced0d228</guid><category><![CDATA[SSH]]></category><category><![CDATA[Hack]]></category><category><![CDATA[Server]]></category><category><![CDATA[Proxy]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Sat, 20 Nov 2021 12:45:39 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1564457461758-8ff96e439e83?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDh8fHNlcnZlcnxlbnwwfHx8fDE2Mzc0MDgwNDI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1564457461758-8ff96e439e83?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDh8fHNlcnZlcnxlbnwwfHx8fDE2Mzc0MDgwNDI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="OpenSSH &#x52A8;&#x6001;&#x589E;&#x51CF;&#x96A7;&#x9053;&#x8F6C;&#x53D1;"><p>&#x6700;&#x8FD1;&#x505A;&#x67D0;&#x6F14;&#x793A;&#xFF0C;&#x671F;&#x95F4;&#x51E0;&#x4E2A; ssh &#x64CD;&#x4F5C;&#x628A;&#x5BF9;&#x65B9;&#x5F00;&#x53D1;&#x7ED9;&#x60CA;&#x5230;&#x4E86;&#xFF0C;&#x6211;&#x624D;&#x53D1;&#x73B0; OpenSSH &#x6709;&#x4E9B;&#x5947;&#x6280;&#x6DEB;&#x5DE7;&#x5E76;&#x4E0D;&#x662F;&#x6240;&#x6709;&#x5F00;&#x53D1;&#x90FD;&#x77E5;&#x9053;&#x7684;&#xFF0C;&#x7279;&#x6765;&#x6C34;&#x4E0A;&#x4E00;&#x7BC7;&#x3002;</p>
<p>&#x51E0;&#x5E74;&#x524D;&#x7FFB; OpenSSH &#x6587;&#x6863;&#x65F6;&#x65E0;&#x610F;&#x4E2D;&#x53D1;&#x73B0;&#xFF0C;&#x96A7;&#x9053;&#x8F6C;&#x53D1;&#x53EF;&#x4EE5;&#x5728;&#x73B0;&#x6709;&#x8FDE;&#x63A5;&#x4E0A;&#x52A8;&#x6001;&#x8BBE;&#x7F6E;&#xFF0C;&#x800C;&#x65E0;&#x9700;&#x91CD;&#x5F00;&#x6216;&#x8005;&#x53E6;&#x5F00;&#x8FDE;&#x63A5;&#x3002;&#x770B;&#x4E86;&#x770B;&#x73B0;&#x5728;&#x4E2D;&#x6587;&#x4E92;&#x8054;&#x7F51;&#x4E0A;&#x5173;&#x4E8E; ssh &#x96A7;&#x9053;&#x7684;&#x4E00;&#x4E9B;&#x6587;&#x7AE0;&#xFF0C;&#x90FD;&#x6CA1;&#x89C1;&#x5230;&#x6709;&#x5173;&#x52A8;&#x6001;&#x8BBE;&#x7F6E;&#x7684;&#x8BF4;&#x660E;&#xFF0C;&#x90FD;&#x9ED8;&#x8BA4;&#x5F00;&#x542F;&#x96A7;&#x9053;&#x9700;&#x53E6;&#x5EFA;&#x6216;&#x91CD;&#x5EFA;&#x65B0;&#x7684; ssh &#x8FDE;&#x63A5;&#x3002;&#x5176;&#x5B9E;&#x4E0D;&#x7136;&#xFF0C;OpenSSH &#x5BA2;&#x6237;&#x7AEF;/&#x670D;&#x52A1;&#x7AEF;&#x652F;&#x6301;&#x5728;&#x4FDD;&#x6301;&#x8FDE;&#x63A5;&#x7684;&#x540C;&#x65F6;&#x589E;&#x5220;&#x96A7;&#x9053;&#x3002;&#x67D0;&#x4E9B;&#x7279;&#x6B8A;&#x573A;&#x666F;&#x4E0B;&#xFF0C;&#x5728;&#x6280;&#x672F;&#x9632;&#x62A4;&#x4E0D;&#x5230;&#x4F4D;&#x7684;&#x73AF;&#x5883;&#x91CC;&#x7528;&#x597D;&#x8FD9;&#x70B9;&#x53EF;&#x4EE5;&#x505A;&#x51FA;&#x96A7;&#x9053;&#x5D4C;&#x5957;&#xFF0C;&#x7AEF;&#x53E3;&#x5C01;&#x9501;&#x9003;&#x9038;&#x7B49;&#x7B49;&#x5404;&#x79CD;&#x9A9A;&#x64CD;&#x4F5C;&#x1F910;&#x3002;</p>
<p>&#x5177;&#x4F53;&#x64CD;&#x4F5C;&#xFF1A;&#x5DF2;&#x7ECF;&#x8FDE;&#x4E0A; ssh &#x65F6;&#xFF0C;&#x6309;&#x4E00;&#x4E0B; <kbd>Enter</kbd>&#xFF0C;&#x518D;&#x6309; <kbd>Shift</kbd>+<kbd>`</kbd>&#xFF08;&#x4E5F;&#x5C31;&#x662F; <kbd>~</kbd>&#xFF09;&#xFF0C;&#x6700;&#x540E;&#x6309; <kbd>Shift</kbd>+<kbd>c</kbd> &#x8C03;&#x51FA; OpenSSH &#x5BA2;&#x6237;&#x7AEF;&#x7684;&#x5185;&#x7F6E;&#x547D;&#x4EE4;&#x884C;&#xFF0C;&#x8F93;&#x5165; <code>help</code> &#x5E2E;&#x52A9;&#xFF0C;&#x8F93;&#x51FA;&#x6240;&#x6709;&#x96A7;&#x9053;&#x64CD;&#x4F5C;&#x7684;&#x547D;&#x4EE4;&#x8BF4;&#x660E;&#x5982;&#x4E0B;&#x3002;</p>
<p><img src="https://xcel.me/content/images/2021/11/Snipaste_2021-11-20_19-57-31.png" alt="OpenSSH &#x52A8;&#x6001;&#x589E;&#x51CF;&#x96A7;&#x9053;&#x8F6C;&#x53D1;" loading="lazy"></p>
<p>&#x63A5;&#x4E0B;&#x6765;&#x8BE5;&#x600E;&#x4E48;&#x5F00;&#x542F;&#x5404;&#x7C7B;&#x96A7;&#x9053;&#x4E0D;&#x5FC5;&#x6211;&#x8D58;&#x8FF0;&#x4E86;&#xFF0C;&#x518D;&#x6B21;&#x8C03;&#x51FA; ssh &#x5BA2;&#x6237;&#x7AEF;&#x7684;&#x5185;&#x7F6E;&#x547D;&#x4EE4;&#x884C;&#xFF0C;&#x8F93;&#x5165;&#x76F8;&#x5173;&#x53C2;&#x6570;&#x56DE;&#x8F66;&#x5373;&#x53EF;&#x3002;</p>
<p><img src="https://xcel.me/content/images/2021/11/Snipaste_2021-11-20_20-24-38-1.png" alt="OpenSSH &#x52A8;&#x6001;&#x589E;&#x51CF;&#x96A7;&#x9053;&#x8F6C;&#x53D1;" loading="lazy"></p>
<p>&#x8FD8;&#x6709;&#x4E00;&#x4E9B;&#x5176;&#x4ED6;&#x64CD;&#x4F5C;&#xFF0C;&#x641C;&#x7D22; &quot;ssh escaping sequences&quot; &#x5373;&#x53EF;&#x3002;&#x4F8B;&#x5982; <kbd>Enter</kbd>&#xFF0C;<kbd>~</kbd>&#xFF0C;<kbd>.</kbd> &#x5F3A;&#x884C;&#x65AD;&#x5F00; ssh &#x8FDE;&#x63A5;&#xFF0C;<kbd>Enter</kbd>&#xFF0C;<kbd>~</kbd>&#xFF0C;<kbd>Ctrl</kbd>+<kbd>z</kbd> &#x628A; ssh &#x5BA2;&#x6237;&#x7AEF;&#x7F6E;&#x4E8E;&#x540E;&#x53F0;&#x8FD0;&#x884C;&#x7B49;&#x7B49;&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Apache Spark 用比特位操作处理状态标志类数据的一种高效实践]]></title><description><![CDATA[前段时间遇到一个略有挑战性的数据需求，要求高速处理巨量的标志位数据，费了些折腾功夫在 Spark 里用 SQL 的二进制操作给实现了个。]]></description><link>https://xcel.me/apache-spark-bitwise-sql-functions-to-accelerate-state-data-storage-and-extraction/</link><guid isPermaLink="false">611e82621cf30264ec9e1f59</guid><category><![CDATA[Python]]></category><category><![CDATA[Spark]]></category><category><![CDATA[SQL]]></category><category><![CDATA[Binary]]></category><category><![CDATA[Bitwise]]></category><category><![CDATA[Hack]]></category><category><![CDATA[programming]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Sat, 21 Aug 2021 16:55:17 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1421986527537-888d998adb74?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIwfHxzcGFya3xlbnwwfHx8fDE2Mjk0Nzg0ODk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1421986527537-888d998adb74?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDIwfHxzcGFya3xlbnwwfHx8fDE2Mjk0Nzg0ODk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Apache Spark &#x7528;&#x6BD4;&#x7279;&#x4F4D;&#x64CD;&#x4F5C;&#x5904;&#x7406;&#x72B6;&#x6001;&#x6807;&#x5FD7;&#x7C7B;&#x6570;&#x636E;&#x7684;&#x4E00;&#x79CD;&#x9AD8;&#x6548;&#x5B9E;&#x8DF5;"><p>&#x524D;&#x6BB5;&#x65F6;&#x95F4;&#x9047;&#x5230;&#x4E00;&#x4E2A;&#x7565;&#x6709;&#x6311;&#x6218;&#x6027;&#x7684;&#x6570;&#x636E;&#x9700;&#x6C42;&#xFF0C;&#x8981;&#x6C42;&#x9AD8;&#x901F;&#x5904;&#x7406;&#x5DE8;&#x91CF;&#x7684;&#x6807;&#x5FD7;&#x4F4D;&#x6570;&#x636E;&#x3002;&#x6211;&#x8D39;&#x4E86;&#x4E9B;&#x6298;&#x817E;&#x529F;&#x592B;&#x5728; Spark &#x91CC;&#x7528; SQL &#x7684;&#x4E8C;&#x8FDB;&#x5236;&#x64CD;&#x4F5C;&#x7ED9;&#x5B9E;&#x73B0;&#x4E86;&#x4E2A;&#xFF0C;&#x4F46;&#x603B;&#x89C9;&#x5F97;&#x5E94;&#x8BE5;&#x6709;&#x66F4;&#x4F18;&#x96C5;&#x7684;&#x5B9E;&#x73B0;&#xFF0C;&#x6709;&#x4E00;&#x79CD;&#x52A8;&#x4F5C;&#x53D8;&#x5F62;&#x7684;&#x611F;&#x89C9;&#xFF0C;&#x867D;&#x7136;&#x76EE;&#x524D;&#x8FD8;&#x633A;&#x582A;&#x7528;&#x3002;</p>
<p>&#x4E0B;&#x8FB9;&#x8FD9;&#x662F;&#x5927;&#x81F4;&#x9700;&#x6C42;&#x3002;&#x6709;&#x4E2A;&#x5927;&#x5BBD;&#x8868; T&#xFF08;&#x5927;&#x7EA6;&#x4E0D;&#x8D85;&#x8FC7; 5k &#x5217;&#xFF09;&#xFF0C;&#x5BF9; T &#x7684;&#x6BCF;&#x6761;&#x6570;&#x636E;&#x505A;&#x67D0;&#x79CD;&#x8BA1;&#x7B97;&#x53EF;&#x4EE5;&#x5F97;&#x5230;&#x5B83;&#x4EEC;&#x7684;&#x4E00;&#x4E9B;&#x72B6;&#x6001;&#xFF0C;&#x72B6;&#x6001;&#x7684;&#x79CD;&#x7C7B;&#x662F;&#x56FA;&#x5B9A;&#x7684;&#xFF0C;&#x6BD4;&#x5982;&#x8FD9;&#x91CC;&#x7684;&#x6570;&#x636E;&#x53EA;&#x80FD;&#x6709; A&#x3001;B&#x3001;C&#x3001;D &#x56DB;&#x79CD;&#x72B6;&#x6001;&#x4E2D;&#x7684;&#x4E00;&#x79CD;&#x6216;&#x591A;&#x79CD;&#x3002;&#x73B0;&#x5728;&#x8981;&#x6C42;&#x4FDD;&#x5B58; T &#x6BCF;&#x4E00;&#x5217;&#x6BCF;&#x4E00;&#x884C;<em>&#x6240;&#x6709;</em>&#x6570;&#x636E;&#x7684;&#x72B6;&#x6001;&#xFF0C;&#x4EE5;&#x4FBF;&#x80FD;&#x88AB;&#x540E;&#x7EED;&#x6B65;&#x9AA4;&#x7EE7;&#x7EED;&#x4F7F;&#x7528;&#xFF0C;&#x5728;&#x4FDD;&#x8BC1;&#x6570;&#x636E;&#x5B8C;&#x6574;&#x6027;&#x7684;&#x540C;&#x65F6;&#xFF0C;&#x9664;&#x4E86;&#x4E0D;&#x80FD;&#x8BA9;&#x7ED3;&#x679C;&#x6570;&#x636E;&#x8FC7;&#x5206;&#x81A8;&#x80C0;&#xFF0C;&#x8FD8;&#x8981;&#x51CF;&#x5C11;&#x5728;&#x540E;&#x7EED;&#x6B65;&#x9AA4;&#x91CC;&#x7684;&#x989D;&#x5916; io &#x5F00;&#x9500;/&#x8BA1;&#x7B97;&#x5F00;&#x9500;&#xFF0C;&#x907F;&#x514D;&#x5F71;&#x54CD;&#x6B63;&#x5E38;&#x4E1A;&#x52A1;&#x2026;&#x2026;&#x5927;&#x6982;&#x8FD9;&#x5C31;&#x662F;&#x65E2;&#x8981;&#x9A6C;&#x513F;&#x8DD1;&#x53C8;&#x8981;&#x9A6C;&#x513F;&#x4E0D;&#x5403;&#x8349;&#x1F602;&#xFF1F;</p>
<p>&#x4E00;&#x822C;&#x5BF9;&#x7B80;&#x5355;&#x4E0D;&#x662F;&#x975E;&#x5E38;&#x5927;&#x91CF;&#x7684;&#x6570;&#x636E;&#xFF0C;&#x7EBF;&#x4E0A;&#x903B;&#x8F91;&#x662F;&#x5E72; json &#x5C31;&#x5B8C;&#x4E86;&#xFF0C;&#x56E0;&#x4E3A;&#x6570;&#x636E;&#x91CF;&#x4E0D;&#x5927;&#x7684;&#x7F18;&#x6545;&#xFF0C;&#x51E0;&#x4E4E;&#x4E0D;&#x4F1A;&#x6709;&#x4EC0;&#x4E48;&#x5E8F;&#x5217;&#x5316;&#x53CD;&#x5E8F;&#x5217;&#x5316;&#x538B;&#x7F29;&#x89E3;&#x538B;&#x7F29;&#x7684;&#x5F00;&#x9500;&#xFF0C;&#x5E76;&#x4E14; json &#x672C;&#x8EAB;&#x662F;&#x8089;&#x773C;&#x53EF;&#x8BFB;&#x6570;&#x636E;&#xFF0C;&#x5BF9;&#x6240;&#x6709;&#x4EBA;&#x90FD;&#x65B9;&#x4FBF;&#x3002;</p>
<p>&#x5927;&#x5BBD;&#x8868;&#x91CC;&#x518D;&#x7528; json &#x663E;&#x7136;&#x662F;&#x4E0D;&#x53EF;&#x884C;&#x7684;&#xFF0C;&#x4E1A;&#x52A1;&#x5185;&#x5C11;&#x8BF4;&#x5C31;&#x6709;&#x4E0A;&#x767E;&#x5217;&#x3001;&#x81F3;&#x5C11;&#x4E0A;&#x5343;&#x4E07;&#x884C;&#x7684;&#x72B6;&#x6001;&#x8981;&#x5904;&#x7406;&#xFF0C;&#x53EF;&#x80FD;&#x9664;&#x4E86;&#x4EE3;&#x7801;&#x6BD4;&#x8F83;&#x7B80;&#x5355;&#x597D;&#x5199;&#x4EE5;&#x5916;&#xFF0C;&#x5149;&#x5E8F;&#x5217;&#x5316;&#x53CD;&#x5E8F;&#x5217;&#x5316;&#x7684;&#x6027;&#x80FD;&#x635F;&#x8017;&#x5C31;&#x591F;&#x96BE;&#x53D7;&#x4E86;&#x3002;&#x5BF9;&#x8FD9;&#x79CD;&#x65F6;&#x95F4;/&#x7A7A;&#x95F4;&#x4E24;&#x96BE;&#x7684;&#x5883;&#x5730;&#xFF0C;&#x54B1;&#x7B2C;&#x4E00;&#x53CD;&#x5E94;&#x662F;&#x81EA;&#x5B9A;&#x4E49;&#x5E95;&#x5C42;&#x6570;&#x636E;&#x7ED3;&#x6784;&#x3002;&#x7136;&#x800C;&#x5BF9; Spark &#x8FD9;&#x6837;&#x56FA;&#x5B9A;&#x5C01;&#x88C5;&#x4E86;&#x6570;&#x636E;&#x7C7B;&#x578B;&#x7684;&#x6846;&#x67B6;&#x6765;&#x8BB2;&#xFF0C;&#x6539;&#x6E90;&#x7801;&#x53BB;&#x5B9A;&#x4E49;&#x65B0;&#x7C7B;&#x578B;&#x663E;&#x7136;&#x4E0D;&#x662F;&#x6700;&#x4F18;&#x89E3;&#x3002;&#x6240;&#x4EE5;&#x8F6C;&#x5FF5;&#x60F3;&#x6709;&#x6CA1;&#x6709;&#x53EF;&#x80FD;&#x7528;&#x5DF2;&#x6709;&#x6570;&#x636E;&#x7C7B;&#x578B;&#xFF0C;&#x505A;&#x4E00;&#x4E2A;&#x81EA;&#x5B9A;&#x4E49;&#x7684;&#x62BD;&#x8C61;&#x7C7B;&#x578B;&#x5462;&#xFF1F;&#x7FFB;&#x4E86;&#x7FFB; Spark <a href="https://spark.apache.org/docs/latest/sql-ref-datatypes.html">&#x6570;&#x636E;&#x7C7B;&#x578B;&#x76F8;&#x5173;&#x7684;&#x6587;&#x6863;</a>&#xFF0C;&#x6240;&#x6709;&#x57FA;&#x7840;&#x539F;&#x59CB;&#x7C7B;&#x578B;&#x90FD;&#x6709;&#x56FA;&#x5B9A;&#x957F;&#x5EA6;&#x3001;&#x56FA;&#x5B9A;&#x8303;&#x56F4;&#xFF0C;&#x55EF;&#x55EF;&#x5F88;&#x597D;&#xFF0C;&#x8FD9;&#x4E9B;&#x7C7B;&#x578B;&#x5B9A;&#x4E49;&#x662F;&#x7EDF;&#x4E00;&#x7684;&#x3002;&#x518D;&#x53BB;<a href="https://spark.apache.org/docs/latest/api/sql/">&#x7FFB;&#x4E86;&#x7FFB; SQL &#x5C42;&#x6587;&#x6863;</a>&#xFF0C;&#x770B;&#x770B;&#x6709;&#x54EA;&#x4E9B;&#x53EF;&#x4EE5;&#x64CD;&#x4F5C;&#x539F;&#x59CB;&#x7C7B;&#x578B;&#x7684; SQL &#x51FD;&#x6570;&#xFF0C;&#x8BF6;&#x4E0D;&#x9519;&#xFF0C;&#x5DE6;&#x53F3;&#x4F4D;&#x79FB;&#xFF0C;&#x4E0E;&#x6216;&#x975E;&#x5F02;&#x6216;&#x4E4B;&#x7C7B;&#x4E5F;&#x9F50;&#x6D3B;&#x4E86;&#x2026;&#x2026;</p>
<h2 id="0-%E5%AE%9A%E4%B9%89%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84">0. &#x5B9A;&#x4E49;&#x6570;&#x636E;&#x7ED3;&#x6784;</h2>
<p>&#x7531;&#x4E8E;&#x72B6;&#x6001;&#x79CD;&#x7C7B;&#x662F;&#x56FA;&#x5B9A;&#x7684;&#xFF0C;&#x5BF9;&#x6BCF;&#x6761;&#x6570;&#x636E;&#x800C;&#x8A00;&#x6240;&#x6709;&#x53EF;&#x80FD;&#x51FA;&#x73B0;&#x72B6;&#x6001;&#x7684;&#x60C5;&#x5F62;&#x4E5F;&#x662F;&#x56FA;&#x5B9A;&#x4E86;&#xFF0C;&#x4E0B;&#x9762;&#x5C31;&#x81EA;&#x7136;&#x60F3;&#x5230;&#x53EF;&#x4EE5;&#x7528;&#x7C7B;&#x4F3C; bitset &#x7684;&#x65B9;&#x6CD5;&#x53BB;&#x505A;&#x72B6;&#x6001;&#x7684;&#x5B58;&#x50A8;&#xFF0C;&#x5229;&#x7528;&#x7387;&#x9AD8;&#xFF0C;&#x89E3;&#x6790;&#x8BFB;&#x53D6;&#x51E0;&#x4E4E;&#x6CA1;&#x6709;&#x8BA1;&#x7B97;&#x91CF;&#xFF0C;&#x76F8;&#x5BF9;&#x6765;&#x8BB2;&#x5BF9;&#x65F6;&#x95F4;&#x7A7A;&#x95F4;&#x90FD;&#x53CB;&#x597D;&#x3002;&#x8FD8;&#x662F; ABCD &#x56DB;&#x79CD;&#x72B6;&#x6001;&#x4E3A;&#x4F8B;&#xFF0C;&#x6211;&#x4EEC;&#x7528; 1 &#x8868;&#x793A;&#x67D0;&#x72B6;&#x6001;&#x5B58;&#x5728;&#xFF0C;0 &#x8868;&#x793A;&#x67D0;&#x72B6;&#x6001;&#x4E0D;&#x5B58;&#x5728;&#x3002;&#x6BD4;&#x5982;&#x8BF4; <code>0000</code> &#x8868;&#x793A; ABCD &#x56DB;&#x79CD;&#x72B6;&#x6001;&#x90FD;&#x6CA1;&#x6709;&#xFF0C;<code>1000</code> &#x8868;&#x793A;&#x53EA;&#x6709; A&#xFF0C;<code>1001</code> &#x8868;&#x793A;&#x6709; A &#x548C; D&#xFF0C;<code>0110</code> &#x8868;&#x793A;&#x6709; BC&#xFF0C;<code>1111</code> &#x8868;&#x793A; ABCD &#x90FD;&#x6709;&#x2026;&#x2026;</p>
<h2 id="1-%E5%AE%9A%E4%B9%89%E5%AD%98%E5%82%A8%E6%96%B9%E6%B3%95">1. &#x5B9A;&#x4E49;&#x5B58;&#x50A8;&#x65B9;&#x6CD5;</h2>
<p>&#x73B0;&#x5728;&#x53EF;&#x4EE5;&#x7528; 4 &#x4E2A;&#x6BD4;&#x7279;&#x4F4D;&#x8868;&#x793A;&#x4E00;&#x4E2A;&#x6570;&#x636E;&#x7684;&#x72B6;&#x6001;&#xFF0C;&#x90A3;&#x4E48;&#x95EE;&#x9898;&#x53C8;&#x6765;&#x4E86;&#xFF0C;&#x600E;&#x4E48;&#x624D;&#x80FD;&#x628A;&#x8FD9;&#x4E9B;&#x72B6;&#x6001;&#x5B58;&#x5165;&#x539F;&#x6709;&#x8868;&#x4E2D;&#x5462;&#xFF1F;Spark DataFrame &#x91CC;&#x663E;&#x7136;&#x6CA1;&#x6709;&#x56DB;&#x4E2A;&#x6BD4;&#x7279;&#x4F4D;&#xFF08;&#x534A;&#x4E2A;&#x5B57;&#x8282;&#xFF09;&#x7684;&#x57FA;&#x7840;&#x7C7B;&#x578B;&#xFF0C;&#x90A3;&#x6211;&#x4EEC;&#x62FF;&#x4E2A;&#x5E38;&#x7528;&#x7684;&#x6574;&#x578B; <code>IntegerType</code> &#x6765;&#x7528;&#x597D;&#x5566;&#xFF0C;32 &#x4F4D;&#x7684;&#x6570;&#x636E;&#xFF0C;&#x5B58;&#x56DB;&#x4E2A;&#x6BD4;&#x7279;&#x7EF0;&#x7EF0;&#x6709;&#x4F59;&#x3002;&#x4E0B;&#x8FB9;&#x62FF; pyspark &#x4E3E;&#x4F8B;&#x8BF4;&#x660E;&#xFF1A;</p>
<pre><code class="language-python">&gt;&gt;&gt; from pyspark.sql import (
...     SparkSession,
...     functions as F,
...     types as T,
... )

# &#x521B;&#x5EFA; session
&gt;&gt;&gt; spark = SparkSession.builder.appName(&apos;app&apos;).getOrCreate()

# &#x8BFB;&#x5165;&#x539F;&#x8868;&#x6587;&#x4EF6;
&gt;&gt;&gt; dataframe = spark.read.parquet(&apos;/path/to/file&apos;)

# &#x5047;&#x5B9A;&#x53EA;&#x6709;&#x4E09;&#x5217;
&gt;&gt;&gt; dataframe.columns
[&apos;column1&apos;, &apos;column2&apos;, &apos;column3&apos;]

# &#x5047;&#x8BBE;&#x8BA1;&#x7B97;&#x7B2C;&#x4E00;&#x5217;&#x56DB;&#x79CD;&#x72B6;&#x6001;&#x7684; sql &#x5982;&#x4E0B;&#xFF0C;
# &#x6BCF;&#x6761; sql &#x8FD4;&#x56DE; 0 &#x6216; 1&#xFF0C;&#x8FD4;&#x56DE; 1 &#x8868;&#x793A;&#x67D0;&#x72B6;&#x6001;&#x5B58;&#x5728;&#xFF0C;0 &#x5219;&#x4E0D;&#x5B58;&#x5728;
# column2, column3 &#x5176;&#x4ED6;&#x4E24;&#x5217;&#x4EE5;&#x6B64;&#x7C7B;&#x63A8;
&gt;&gt;&gt; column1_A_sql = F.when(..., 1).otherwise(0)
&gt;&gt;&gt; column1_B_sql = F.when(..., 1).otherwise(0)
&gt;&gt;&gt; column1_C_sql = F.when(..., 1).otherwise(0)
&gt;&gt;&gt; column1_D_sql = F.when(..., 1).otherwise(0)

# &#x56DB;&#x4E2A;&#x72B6;&#x6001;&#x4F4D;&#x5408;&#x5E76;&#x5230;&#x4E00;&#x4E2A; IntegerType &#x6574;&#x578B;&#x4E2D;
&gt;&gt;&gt; column1_states = F.lit(0) \
...     .bitwiseOR(F.shiftLeft(columns1_A_sql, 3)) \
...     .bitwiseOR(F.shiftLeft(columns1_B_sql, 2)) \
...     .bitwiseOR(F.shiftLeft(columns1_C_sql, 1)) \
...     .bitwiseOR(F.shiftLeft(columns1_D_sql, 0)) \
...     .astype(T.IntegerType())

# &#x7C7B;&#x63A8;&#x5F97;&#x5230;&#x53E6;&#x4E24;&#x5217;&#x7684;&#x5B8C;&#x6574;&#x72B6;&#x6001; column2_states, column3_states&#xFF0C;
# &#x6267;&#x884C;&#x4EE5;&#x4E0A; sql &#x5F97;&#x5230;&#x5305;&#x542B;&#x4E09;&#x5217;&#x5B8C;&#x6574;&#x72B6;&#x6001;&#x7684; df `states`
&gt;&gt;&gt; states = dataframe.select(
...     column1_states.alias(&apos;column1_states&apos;)
...     column2_states.alias(&apos;column2_states&apos;)
...     column3_states.alias(&apos;column3_states&apos;)
... )
&gt;&gt;&gt; states.show()
+--------------+--------------+--------------+
|column1_states|column2_states|column3_states|
+--------------+--------------+--------------+
|             1|             4|             7|
|             2|             5|             8|
|             3|             6|             9|
|           ...|           ...|           ...|
+--------------+--------------+--------------+
</code></pre>
<h2 id="2-%E5%AE%9A%E4%B9%89%E8%AF%BB%E5%8F%96%E8%A7%A3%E6%9E%90%E6%96%B9%E6%B3%95">2. &#x5B9A;&#x4E49;&#x8BFB;&#x53D6;/&#x89E3;&#x6790;&#x65B9;&#x6CD5;</h2>
<p>&#x6BD4;&#x7279;&#x5B58;&#x50A8;&#x7684;&#x6807;&#x8BC6;&#x4F4D;&#x6570;&#x636E;&#xFF0C;&#x8BFB;&#x53D6;&#x8D77;&#x6765;&#x4E5F;&#x5F88;&#x65B9;&#x4FBF;&#x3002;</p>
<pre><code class="language-python"># &#x6BCF;&#x79CD;&#x6807;&#x8BC6;&#x4F4D;&#x6240;&#x5728;&#x7684;&#x987A;&#x5E8F;
&gt;&gt;&gt; state_index = {&apos;A&apos;: 3, &apos;B&apos;: 2, &apos;C&apos;: 1, &apos;D&apos;: 0}

# &#x5B9A;&#x4E49;&#x4ECE;&#x4E00;&#x5217;&#x4E2D;&#x8BFB;&#x53D6;&#x67D0;&#x79CD;&#x6807;&#x8BC6;&#x72B6;&#x6001;&#x7684;&#x51FD;&#x6570;
&gt;&gt;&gt; def get_state_x(states_dataframe, state_column, state_type):
...     # &#x83B7;&#x53D6;&#x72B6;&#x6001;&#x5BF9;&#x5E94;&#x7684;&#x987A;&#x5E8F;&#x4F4D;&#xFF0C;&#x72B6;&#x6001;&#x540D;&#x4E0D;&#x5BF9;&#x65F6;&#x629B;&#x51FA;&#x9519;&#x8BEF;
...     index = state_index.get(state_type)
...     if index is None:
...         raise Exception(f&apos;Unknown state type of {state_type}&apos;)
...
...     # &#x8FC7;&#x6EE4;&#x6389;&#x5176;&#x4ED6;&#x72B6;&#x6001;&#x4F4D;
...     masked_state = F.col(state_column).bitwiseAND(1 &lt;&lt; index)
...
...     # &#x5224;&#x65AD;&#x5269;&#x4E0B;&#x7684;&#x72B6;&#x6001;&#x4F4D;&#x4E0A;&#x662F;&#x5426;&#x8FD8;&#x6709; 1&#xFF0C;&#x5982;&#x679C;&#x53EA;&#x5269; 0&#xFF0C;&#x8868;&#x660E;&#x5F53;&#x524D;&#x72B6;&#x6001;&#x4E0D;&#x5B58;&#x5728;&#xFF0C;&#x5426;&#x5219;&#x4E3A;&#x5B58;&#x5728;
...     result = F.when(masked_state == F.lit(0), F.lit(False)) \
...         .otherwise(F.lit(True))
...
...     # &#x5BF9;&#x72B6;&#x6001; df &#x6267;&#x884C; sql
...     return states_dataframe.select(result.alias(f&apos;{state_type}_exists&apos;))
...

# &#x7EE7;&#x7EED;&#x62FF;&#x4E0A;&#x8FB9;&#x5F97;&#x5230;&#x7684; states &#x4E3E;&#x4F8B;&#xFF0C;&#x89E3;&#x6790; column1 &#x4E2D;&#x662F;&#x5426;&#x6709; D &#x72B6;&#x6001;
&gt;&gt;&gt; get_state_x(states, &apos;column1_states&apos;, &apos;D&apos;).show()
+--------+
|D_exists|
+--------+
|    true|
|   false|
|    true|
|     ...|
+--------+
</code></pre>
<p>&#x5230;&#x8FD9;&#x91CC;&#x5C31;&#x5B8C;&#x6210;&#x4E86;&#x5B8C;&#x6574;&#x6D41;&#x7A0B;&#x3002;</p>
<h2 id="3-%E4%BC%98%E5%8C%96%E6%94%B9%E8%BF%9B">3. &#x4F18;&#x5316;&#x6539;&#x8FDB;</h2>
<p>&#x4E0A;&#x8FB9;&#x7684;&#x5B9E;&#x73B0;&#x65B9;&#x5F0F;&#x6709;&#x4E00;&#x4E2A;&#x660E;&#x663E;&#x53EF;&#x6539;&#x8FDB;&#x4E4B;&#x5904; &#x2014;&#x2014; &#x4F7F;&#x7528; 32 &#x4F4D;&#x7684; <code>IntegerType</code> &#x5B58;&#x50A8; 4 &#x4F4D;&#x7684;&#x6570;&#x636E;&#xFF0C;&#x53EA;&#x5B58;&#x4E86;&#x4E00;&#x5217;&#x7684;&#x72B6;&#x6001;&#xFF0C;&#x5269;&#x4F59; 28 &#x4F4D;&#x6D6A;&#x8D39;&#x6389;&#x4E86;&#xFF0C;&#x662F;&#x4E0D;&#x662F;&#x53EF;&#x4EE5;&#x5728;&#x4E00;&#x4E2A; 32 &#x4F4D;&#x6574;&#x578B;&#x4E2D;&#x6700;&#x591A;&#x585E;&#x5165; 8 &#x5217;&#x7684;&#x72B6;&#x6001;&#xFF0C;&#x76F4;&#x63A5;&#x63D0;&#x5347; 7 &#x500D;&#x7684;&#x5229;&#x7528;&#x7387;&#x5462;&#xFF1F;&#x8FD9;&#x601D;&#x8DEF;&#x662F;&#x53EF;&#x884C;&#x7684;&#xFF0C;&#x6211;&#x4EEC;&#x751A;&#x81F3;&#x8FD8;&#x53EF;&#x4EE5;&#x628A;&#x6574;&#x8868;&#x7684;&#x72B6;&#x6001;&#x5168;&#x585E;&#x5165;&#x4E00;&#x5217;&#x4E2D;</p>
<pre><code class="language-python">from pyspark.sql import SparkSession
from pyspark.sql import functions as F
from pyspark.sql import types as T
from more_itertools import ichunked

spark = SparkSession.builder.appName(&apos;app&apos;).getOrCreate()

# &#x5047;&#x8BBE;&#x8FD9;&#x6B21;&#x539F;&#x8868;&#x6709; 1000 &#x5217;&#x6570;&#x636E;&#x7684;&#x72B6;&#x6001;&#x8981;&#x5904;&#x7406;, column1, column2, column3, ...
table = spark.read.parquet(&apos;/path/to/table&apos;)

# &#x5047;&#x8BBE;&#x8BA1;&#x7B97;&#x4E00;&#x5217;&#x4E2D;&#x67D0;&#x72B6;&#x6001;&#x7684; sql &#x51FD;&#x6570;&#x5982;&#x4E0B;&#xFF0C;&#x540C;&#x4E4B;&#x524D;&#x4E00;&#x6837;&#xFF0C;&#x4ECD;&#x4E3A;&#x8FD4;&#x56DE; 0 &#x6216; 1 &#x7684; sql
def column_state_A(column)&#xFF1A;
    ...
    
def column_state_B(column):
    ...
    
def column_state_C(column):
    ...

def column_state_D(column):
    ...
    
# &#x83B7;&#x53D6;&#x5168;&#x8868;&#x72B6;&#x6001;&#x7684;&#x51FD;&#x6570;&#xFF0C;&#x4E00;&#x5217;&#x6709; ABCD &#x56DB;&#x79CD;&#x72B6;&#x6001;&#xFF0C;&#x516B;&#x5217;&#x4E00;&#x7EC4;&#xFF0C;&#x5171; 32 &#x4F4D;&#xFF0C;&#x5B58;&#x5165;&#x4E00;&#x4E2A; 32 &#x4F4D;&#x6574;&#x578B;
# &#x6700;&#x540E;&#x628A;&#x6240;&#x6709;&#x6574;&#x6570;&#x505A;&#x6210;&#x4E00;&#x4E2A;&#x6570;&#x7EC4;&#xFF0C;&#x5B58;&#x5165;&#x4E00;&#x4E2A;&#x6570;&#x7EC4;&#x5217;&#x4E2D;
def get_full_states(table):
    # &#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x6574;&#x6570;&#x6570;&#x7EC4;&#x5217;&#xFF0C;&#x7528;&#x6765;&#x5B58;&#x5165;&#x5168;&#x8868;&#x7684;&#x6240;&#x6709;&#x72B6;&#x6001;
    all_states_column = F.array().astype(T.ArrayType(T.IntegerType()))
    
    # &#x6BCF; 8 &#x5217;&#x53D6;&#x4E00;&#x7EC4;&#xFF0C;&#x6BCF;&#x7EC4;&#x751F;&#x6210;&#x4E00;&#x4E2A;&#x6574;&#x578B;&#x5B58;&#x5165;&#x7EC4;&#x5185;&#x6240;&#x6709;&#x72B6;&#x6001;
    for chunk in ichunked(enumerate(table.columns), 8):
        state_int = F.lit(0).astype(T.IntegerType())
        
        # &#x904D;&#x5386;&#x7EC4;&#x5185;&#x6BCF;&#x4E00;&#x5217;
        for column_index, column in chunk:
            # &#x8BA1;&#x7B97;&#x672C;&#x5217;&#x7684;&#x6BCF;&#x79CD;&#x72B6;&#x6001;
            state_A = column_state_A(column)
            state_B = column_state_B(column)
            state_C = column_state_C(column)
            state_D = column_state_D(column)
            
            # column_index &#x4E3A;&#x672C;&#x5217;&#x5728;&#x539F;&#x8868;&#x4E2D;&#x5E8F;&#x53F7;&#xFF0C;
            # &#x8BA1;&#x7B97;&#x672C;&#x5217;&#x5728;&#x672C;&#x7EC4;&#x5185;&#x7684;&#x5E8F;&#x53F7;&#xFF0C;&#x56E0;&#x4E3A;&#x662F; 8 &#x5217;&#x4E00;&#x7EC4;&#xFF0C;&#x6240;&#x4EE5;&#x53EF;&#x5F97;&#x7EC4;&#x5185;&#x5E8F;&#x53F7;&#x5982;&#x4E0B;
            ingroup_index = column_index % 8
            
            # &#x7136;&#x540E;&#x53EF;&#x5F97;&#x672C;&#x5217;&#x6BCF;&#x4E2A;&#x72B6;&#x6001;&#x5728;&#x7EC4;&#x5185;&#x7684;&#x5177;&#x4F53;&#x5E8F;&#x53F7;
            a_index = ingroup_index * 4 + 3
            b_index = ingroup_index * 4 + 2
            c_index = ingroup_index * 4 + 1
            d_index = ingroup_index * 4
            
            # &#x5199;&#x5165;&#x672C;&#x5217;&#x7684;&#x5168;&#x90E8;&#x72B6;&#x6001;&#x5230;&#x7EC4;&#x5185;
            for state in [
                F.shiftLeft(state_A, a_index),
                F.shiftLeft(state_B, b_index),
                F.shiftLeft(state_C, c_index),
                F.shiftLeft(state_D, d_index)
            ]:
                state_int = state_int.bitwiseOR(state)
                
        # &#x4E00;&#x7EC4;&#x7684;&#x72B6;&#x6001;&#x5408;&#x5E76;&#x5230;&#x5168;&#x8868;
        all_states_column = F.concat(
            all_states_column, 
            F.array(state_int.astype(T.IntegerType()))
        )
        
    # &#x8FD4;&#x56DE;&#x751F;&#x6210;&#x5168;&#x8868;&#x72B6;&#x6001;&#x5217;&#x7684;&#x5B8C;&#x6574; sql
    return all_states_column

# &#x751F;&#x6210;&#x6240;&#x9700; sql&#xFF0C;&#x5E76;&#x6267;&#x884C;&#x5F97;&#x5230;&#x5168;&#x8868;&#x7684;&#x72B6;&#x6001;&#x5217;&#xFF0C;&#x653E;&#x5165;&#x8868;&#x4E2D;&#xFF0C;&#x5217;&#x540D;&#x4E3A; all_states
all_states_sql = get_full_states(table)
table = table.withColumn(&apos;all_states&apos;, all_states_sql)
</code></pre>
<p>&#x9884;&#x89C8;&#x770B;&#x4E0B;&#x6700;&#x540E;&#x751F;&#x6210;&#x7684;&#x5168;&#x8868;&#x72B6;&#x6001;&#x5217;&#xFF0C;&#x5927;&#x6982;&#x957F;&#x8FD9;&#x4E2A;&#x6837;</p>
<pre><code class="language-python">&gt;&gt;&gt; table.select(F.col(&apos;all_states&apos;)).show()
+-----------+
| all_states|
+-----------+
|[10 10 ...]|
|  [0 0 ...]|
|  [0 1 ...]|
+-----------+
</code></pre>
<p>&#x6838;&#x5FC3;&#x6B65;&#x9AA4;&#x548C;&#x6539;&#x8FDB;&#x524D;&#x662F;&#x4E00;&#x81F4;&#x7684;&#xFF0C;&#x53EA;&#x662F;&#x591A;&#x4E86;&#x7F29;&#x51CF;&#x4F7F;&#x7528;&#x7A7A;&#x95F4;&#x7684;&#x6B65;&#x9AA4;&#xFF1A;&#x5C06;&#x591A;&#x5217;&#x5408;&#x5E76;&#x4E3A;&#x4E00;&#x7EC4;&#xFF0C;&#x4E00;&#x7EC4;&#x5199;&#x5165;&#x5230;&#x540C;&#x4E00;&#x4E2A;&#x6574;&#x6570;&#xFF0C;&#x6700;&#x540E;&#x5C06;&#x6240;&#x6709;&#x7EC4;&#x7684;&#x6574;&#x6570;&#x5408;&#x5E76;&#x5230;&#x4E00;&#x4E2A;&#x6570;&#x7EC4;&#x5217;&#x4E2D;&#x3002;</p>
<p>&#x63A5;&#x7740;&#x8BFB;&#x53D6;&#x72B6;&#x6001;&#x7684;&#x89E3;&#x6790;&#x51FD;&#x6570;&#x5982;&#x4E0B;&#xFF1A;</p>
<pre><code class="language-python"># &#x63A5;&#x4E0A;&#xFF0C;&#x751F;&#x6210;&#x7684;&#x5168;&#x8868;&#x72B6;&#x6001;&#x5217;&#x4E3A; all_states
# &#x540C;&#x6837;&#xFF0C;&#x56DB;&#x79CD;&#x72B6;&#x6001;&#x7684;&#x76F8;&#x5BF9;&#x987A;&#x5E8F;&#x5E8F;&#x53F7;
state_index = {&apos;A&apos;: 3, &apos;B&apos;: 2, &apos;C&apos;: 1, &apos;D&apos;: 0}

def get_state_x_from_column(table, all_states, column, state):
    &quot;&quot;&quot;
    &#x8BA1;&#x7B97;&#x67D0;&#x5217;&#x4E2D;&#x5305;&#x542B;&#x67D0;&#x72B6;&#x6001;&#x7684;&#x60C5;&#x51B5;
    
    :param table:        &#x5E26;&#x6709;&#x5168;&#x8868;&#x72B6;&#x6001;&#x5217;&#x7684;&#x6574;&#x8868;
    :param all_states:   &#x5168;&#x8868;&#x72B6;&#x6001;&#x5217;&#x7684;&#x5217;&#x540D;
    :param column:       &#x9700;&#x8981;&#x5173;&#x6CE8;&#x7684;&#x5217;&#x540D;
    :param state:        &#x9700;&#x8981;&#x5173;&#x6CE8;&#x7684;&#x72B6;&#x6001;&#x540D;
    &quot;&quot;&quot;
    columns = table.columns
    columns.remove(all_states)
    if column not in columns:
        raise Exception(f&apos;&#x5217;&#x540D; {column} &#x4E0D;&#x6B63;&#x786E;&#xFF0C;&#x8868;&#x4E2D;&#x6CA1;&#x6709;&#x6B64;&#x5217;&apos;)
        
    if state not in state_index:
        raise Exception(f&apos;&#x72B6;&#x6001;&#x540D; {state} &#x4E0D;&#x6B63;&#x786E;&#xFF0C;&#x6CA1;&#x6709;&#x6B64;&#x72B6;&#x6001;&apos;)
        
    # &#x4F7F;&#x7528;&#x672C;&#x5217;&#x5728;&#x5168;&#x8868;&#x4E2D;&#x7684;&#x5E8F;&#x53F7;&#xFF0C;&#x8BA1;&#x7B97;&#x672C;&#x5217;&#x6240;&#x5728;&#x7EC4;&#x7684;&#x7EC4;&#x5E8F;&#x53F7;
    group_index = columns.index(column) // 8
    # &#x8BA1;&#x7B97;&#x672C;&#x5217;&#x5728;&#x7EC4;&#x5185;&#x7684;&#x5E8F;&#x53F7;
    column_ingroup_index = columns.index(column) % 8
    # &#x8BA1;&#x7B97;&#x6240;&#x9700;&#x72B6;&#x6001;&#x5728;&#x7EC4;&#x5185;&#x7684;&#x5E8F;&#x53F7;
    state_ingroup_index = state_index.get(state) + 4 * column_ingroup_index
    
    # &#x6839;&#x636E;&#x4EE5;&#x4E0A;&#x5E8F;&#x53F7;&#x8FC7;&#x6EE4;&#x51FA;&#x6240;&#x9700;&#x72B6;&#x6001;&#x7684;&#x6807;&#x8BC6;&#x4F4D;
    state_masked = F.col(all_states).getItem(group_index).bitwiseAND(1 &lt;&lt; state_ingroup_index)
    
    # &#x6839;&#x636E;&#x6807;&#x8BC6;&#x4F4D;&#x8FD4;&#x56DE; true/false &#x8868;&#x793A;&#x672C;&#x5217;&#x5305;&#x542B;&#x6B64;&#x72B6;&#x6001;&#x7684;&#x60C5;&#x51B5;&#xFF0C;&#x8FD4;&#x56DE;&#x5B8C;&#x6574; sql
    return F.when(state_masked == 0, False).otherwise(True)
</code></pre>
<p>&#x6211;&#x4EEC;&#x770B;&#x4E0B;&#x4F7F;&#x7528;&#x6548;&#x679C;</p>
<pre><code class="language-python">&gt;&gt;&gt; sql = get_state_x_from_column(table, &apos;all_states&apos;, &apos;column1&apos;, &apos;A&apos;)
&gt;&gt;&gt; table.select(sql.alias(&apos;A_in_column1&apos;)).show()
+------------+
|A_in_column1|
+------------+
|        true|
|       false|
|       false|
+------------+
</code></pre>
<p>Perfecto!</p>
<h2 id="4-%E5%85%B6%E4%BB%96">4. &#x5176;&#x4ED6;</h2>
<p>&#x76EE;&#x524D;&#x8FD9;&#x6BB5;&#x4EE3;&#x7801;&#x5DF2;&#x7ECF;&#x4E0A;&#x7EBF;&#xFF0C;&#x5B9E;&#x6D4B;&#x751F;&#x6210;&#x5168;&#x8868;&#x72B6;&#x6001;&#x5360;&#x7528;&#x7684;&#x8BA1;&#x7B97;&#x65F6;&#x95F4;&#x4E3A;&#x4EFB;&#x52A1;&#x603B;&#x7528;&#x65F6;&#x7684; 0.01% ~ 0.1%&#xFF0C;&#x89E3;&#x6790;&#x67D0;&#x5217;&#x72B6;&#x6001;&#x5360;&#x7528;&#x7684;&#x8BA1;&#x7B97;&#x65F6;&#x95F4;&#x7A33;&#x5B9A;&#x5728;&#x4E2A;&#x4F4D;&#x6570;&#x6BEB;&#x79D2;&#xFF0C;&#x51E0;&#x4E4E;&#x53EF;&#x5FFD;&#x7565;&#x4E0D;&#x8BA1;&#x3002;&#x7A7A;&#x95F4;&#x5360;&#x7528;&#x7684;&#x589E;&#x52A0;&#x91CF;&#x540C;&#x6240;&#x9700;&#x72B6;&#x6001;&#x91CF;&#x3001;&#x6570;&#x636E;&#x8868;&#x884C;&#x5217;&#x603B;&#x6570;&#x5448;&#x7B80;&#x5355;&#x7684;&#x7EBF;&#x6027;&#x5173;&#x7CFB;&#xFF0C;&#x540C;&#x8868;&#x6570;&#x636E;&#x672C;&#x8EAB;&#x5173;&#x7CFB;&#x4E0D;&#x5927;&#x3002;</p>
<p>&#x6700;&#x540E;&#x6548;&#x679C;&#x8FD8;&#x4E0D;&#x9519;&#xFF0C;&#x4E0D;&#x8FC7;&#x8BF4;&#x5230;&#x5E95;&#x8FD8;&#x5F97;&#x5F52;&#x529F;&#x4E8E; Spark &#x8FD9;&#x6837;&#x7684;&#x795E;&#x7EA7;&#x5F00;&#x6E90;&#x8F6F;&#x4EF6;&#xFF0C;&#x9ED8;&#x9ED8;&#x5728;&#x80CC;&#x540E;&#x63D0;&#x4F9B;&#x4E86;&#x5982;&#x6B64;&#x5B8C;&#x6574;&#x53C8;&#x5F3A;&#x5927;&#x7684;&#x529F;&#x80FD;&#xFF0C;&#x8BA9;&#x6211;&#x7B49;&#x5C0F;&#x767D;&#x80FD;&#x6DF7;&#x53E3;&#x996D;&#x5403;&#x1F602;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Nginx + Dnsmasq 解锁 Netflix]]></title><description><![CDATA[使用 Nginx 配合 Dnsmasq 解锁 Netflix 的方法，可以让解锁服务和 HTTPS 网站共存。]]></description><link>https://xcel.me/unlock_netflix_with_nginx_and_dnsmasq/</link><guid isPermaLink="false">6118b3df3f6cd53b534b88ef</guid><category><![CDATA[Nginx]]></category><category><![CDATA[Dnsmasq]]></category><category><![CDATA[Proxy]]></category><category><![CDATA[Server]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Sun, 15 Aug 2021 08:56:23 GMT</pubDate><media:content url="https://images.unsplash.com/photo-1522869635100-9f4c5e86aa37?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE0fHxOZXRmbGl4fGVufDB8fHx8MTYyOTAwOTA3Nw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://images.unsplash.com/photo-1522869635100-9f4c5e86aa37?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwxMTc3M3wwfDF8c2VhcmNofDE0fHxOZXRmbGl4fGVufDB8fHx8MTYyOTAwOTA3Nw&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=2000" alt="Nginx + Dnsmasq &#x89E3;&#x9501; Netflix"><p>&#x4E4B;&#x524D; GitHub &#x4E0A;&#x5F88;&#x591A;&#x4EBA;&#x7528;&#x7684; Netflix &#x89E3;&#x9501; Docker &#x5BB9;&#x5668;&#xFF0C;&#x662F;&#x7528;&#x4E00;&#x4E2A;&#x4E13;&#x95E8;&#x7684; sniproxy &#x7A0B;&#x5E8F;&#x201C;&#x53CD;&#x4EE3;&#x201D; HTTPS &#x6D41;&#x91CF;&#x3002;sniproxy &#x7684;&#x597D;&#x5904;&#x662F;&#x914D;&#x7F6E;&#x591F;&#x7B80;&#x5355;&#x7075;&#x6D3B;&#xFF0C;&#x574F;&#x5904;&#x662F;&#x5982;&#x679C;&#x5DF2;&#x7ECF;&#x6709; HTTPS &#x670D;&#x52A1;&#x5728;&#x8FD0;&#x884C;&#xFF0C;&#x90A3; 443 &#x7AEF;&#x53E3;&#x88AB;&#x5360;&#x4E86;&#x662F;&#x6CA1;&#x6CD5;&#x7528; sniproxy &#x7684;&#x3002;&#x672C;&#x7AD9;&#x7528; Nginx &#x53CD;&#x4EE3;&#x540E;&#x7AEF;&#xFF0C;&#x800C; Nginx &#x65E9;&#x5DF2;&#x7ECF;&#x53EF;&#x4EE5;&#x901A;&#x8FC7; stream &#x53D8;&#x76F8;&#x5B9E;&#x73B0; sniproxy &#x7684;&#x529F;&#x80FD;&#xFF0C;&#x4E8E;&#x662F;&#x5C31;&#x6709;&#x4E86;&#x672C;&#x6587;&#x7684;&#x505A;&#x6CD5;&#xFF0C;&#x53EF;&#x4EE5;&#x8BA9;&#x89E3;&#x9501;&#x670D;&#x52A1;&#x548C;&#x666E;&#x901A; HTTPS &#x7F51;&#x7AD9;&#x540C;&#x65F6;&#x8FD0;&#x884C;&#x3002;</p>
<p>&#x5148;&#x8BF4;&#x4E00;&#x4E0B;&#xFF0C;Netflix &#x76EE;&#x524D;&#x5DF2;&#x7ECF;&#x9010;&#x6B65;&#x653E;&#x677E; IP &#x5730;&#x7406;&#x4F4D;&#x7F6E;&#x9650;&#x5236;&#x4E86;&#xFF0C;&#x539F;&#x6765;&#x5B8C;&#x5168;&#x4E0D;&#x80FD;&#x7528;&#x7684;&#x5730;&#x5740;&#x73B0;&#x5728;&#x81F3;&#x5C11;&#x53EF;&#x4EE5;&#x770B;&#x81EA;&#x5236;&#x5267;&#xFF0C;&#x4E5F;&#x6709;&#x5F88;&#x591A;&#x5F88;&#x68D2;&#x7684;&#x5267;&#x96C6;&#x54DF;&#xFF01;&#x7B2C;&#x4E09;&#x65B9;&#x5185;&#x5BB9;&#x91CC;&#x5927;&#x90E8;&#x5206;&#x8FD8;&#x662F;&#x9700;&#x8981;&#x4EE3;&#x7406;&#x89E3;&#x9501;&#xFF0C;&#x6BD4;&#x5982; AMC &#x7684; The Walking Dead&#xFF0C;Better Call Saul &#x7B49;&#x7B49;&#x3002;&#x4E0B;&#x9762;&#x5C31;&#x8BB2;&#x8BB2; DNS &#x89E3;&#x9501; Netflix &#x662F;&#x600E;&#x4E48;&#x4E00;&#x56DE;&#x4E8B;&#x3002;</p>
<h3 id="0-%E5%87%86%E5%A4%87">0. &#x51C6;&#x5907;</h3>
<p>&#x9996;&#x5148;&#xFF0C;&#x6211;&#x4EEC;&#x9700;&#x8981;&#x4E00;&#x4E2A;&#x80FD;&#x770B; Netflix &#x5B8C;&#x6574;&#x5185;&#x5BB9;&#x7684; IP&#xFF0C;&#x5C31;&#x662F;&#x6CA1;&#x6709;&#x88AB;&#x5B83;&#x6807;&#x8BB0;&#x9650;&#x5236;&#x7684;&#x90A3;&#x79CD;&#x2026; &#x55EF;&#xFF0C;&#x65E2;&#x7136;&#x6211;&#x5DF2;&#x7ECF;&#x6709;&#x53EF;&#x4EE5;&#x770B;&#x7684; IP &#x4E86;&#x8FD8;&#x8981;&#x89E3;&#x9501;&#x5E72;&#x5565;&#x5440;&#xFF1F;&#x522B;&#x6025;&#xFF0C;&#x914D;&#x597D;&#x8FD9;&#x4E2A;&#x89E3;&#x9501;&#x53EF;&#x4EE5;&#x7ED9;&#x7535;&#x89C6;&#x7535;&#x8BDD;&#x7535;&#x51B0;&#x7BB1;&#x7535;&#x78C1;&#x7089;&#x7528;&#x6216;&#x8005;&#x7ED9;&#x5206;&#x4EAB;&#x670B;&#x53CB;&#x7B49;&#x7B49;&#x7528;&#xFF0C;&#x800C;&#x4E0D;&#x9700;&#x8981;&#x5B89;&#x88C5;&#x989D;&#x5916;&#x7684;&#x8F6F;&#x4EF6;&#x914D;&#x7F6E;&#x989D;&#x5916;&#x7684;&#x4EE3;&#x7406;&#xFF0C;&#x8DDF;&#x666E;&#x901A;&#x7684;&#x7FFB;&#x5899;&#x4EE3;&#x7406;&#x4E0D;&#x4E00;&#x6837;&#xFF0C;&#x8FD9;&#x91CC;&#x53EA;&#x8BBE;&#x7F6E;&#x4E00;&#x4E2A; DNS &#x5C31;&#x53EF;&#x4EE5;&#x3002;&#x9898;&#x5916;&#x8BDD;&#xFF0C;&#x636E;&#x8BF4;&#x76EE;&#x524D;&#x79C1;&#x6709; IPv6 &#x57FA;&#x672C;&#x6CA1;&#x6709;&#x9650;&#x5236;&#xFF0C;&#x6709;&#x5927;&#x4F6C;&#x81EA;&#x5DF1;&#x6CE8;&#x518C;&#x4E86; ASN &#x4E70;&#x4E86;&#x6BB5; v6 &#x5730;&#x5740;&#x4EB2;&#x6D4B;&#x53EF;&#x7528;&#xFF0C;&#x8FD9;&#x4E2A;&#x8D85;&#x7EB2;&#x4E86;&#xFF0C;&#x4E0D;&#x5728;&#x672C;&#x6587;&#x8BA8;&#x8BBA;&#x8303;&#x56F4;&#x5185;&#x1F602;&#x3002;</p>
<h3 id="1-nginx">1. Nginx</h3>
<p>SSH &#x767B;&#x4E0A;&#x6CA1;&#x6709;&#x88AB; Netflix &#x5C4F;&#x853D;&#x7684;&#x670D;&#x52A1;&#x5668;&#xFF0C;&#x8BBE;&#x7F6E;&#x4E00;&#x4E0B; Nginx&#x3002;&#x53C2;&#x8003; <a href="http://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html">Nginx &#x6709;&#x5173;&#x6587;&#x6863;</a>&#x3002;&#x5982;&#x679C;&#x5DF2;&#x7ECF;&#x88C5;&#x6709; Nginx &#x7684;&#x8BDD;&#xFF0C;<code>nginx -V</code> &#x6267;&#x884C;&#x770B;&#x4E0B;&#x53EF;&#x7528;&#x7684; flag &#x91CC;&#x6709;&#x6CA1;&#x6709;&#x6253;&#x5F00;&#x7F16;&#x8BD1;&#x9009;&#x9879;<br>
<code>--with-stream_ssl_preread_module</code>&#xFF0C;&#x5982;&#x679C;&#x6CA1;&#x6709;&#x7684;&#x8BDD;&#xFF0C;ubuntu &#x53EF;&#x4EE5; apt install &#x5B89;&#x88C5; <code>nginx-extras</code> &#x66FF;&#x6362;&#x6389;&#x539F;&#x6765;&#x7684;&#x7248;&#x672C;&#xFF0C;&#x5B89;&#x88C5;&#x5B8C;&#x5168;&#x4F53; Nginx&#xFF0C;&#x5176;&#x4ED6;&#x53D1;&#x884C;&#x7248;&#x5E94;&#x8BE5;&#x4E5F;&#x6709;&#x7C7B;&#x4F3C;&#x5305;&#xFF0C;&#x5B9E;&#x5728;&#x4E0D;&#x884C;&#x518D;&#x81EA;&#x884C;&#x914D;&#x7F6E;&#x9009;&#x9879;&#x91CD;&#x7F16;&#x8BD1;&#x5427;&#x3002;</p>
<p>Nginx &#x4FEE;&#x6539;&#x914D;&#x7F6E;&#xFF0C;nginx.conf &#x4E2D;&#x627E;&#x5230; <code>stream {}</code> &#x533A;&#x57DF;&#xFF0C;&#x5982;&#x679C;&#x6CA1;&#x6709;&#x7684;&#x8BDD;&#x53EF;&#x4EE5;&#x81EA;&#x5DF1;&#x624B;&#x52A8;&#x6DFB;&#x52A0;&#x3002;&#x91CC;&#x9762;&#x52A0;&#x4E00;&#x4E2A; server &#x7528;&#x6765;&#x201C;&#x53CD;&#x4EE3;&#x201D; HTTPS &#x6570;&#x636E;&#x6D41;&#xFF0C;&#x518D;&#x52A0;&#x4E00;&#x7EC4;&#x6B63;&#x5219;&#x6620;&#x5C04;&#x89C4;&#x5219;&#x901A;&#x8FC7; SNI &#x8FC7;&#x6EE4;&#x57DF;&#x540D;&#xFF0C;&#x50CF;&#x8FD9;&#x6837;&#xFF0C;&#x53EA;&#x5141;&#x8BB8; Netflix &#x57DF;&#x540D;&#x7684; SNI&#xFF1A;</p>
<pre><code>stream {
    # &#x52A0;&#x4E00;&#x7EC4; map&#xFF0C;&#x6839;&#x636E;&#x89C4;&#x5219;&#x8FC7;&#x6EE4; $ssl_preread_server_name &#xFF08;&#x4E5F;&#x5C31;&#x662F;&#x8BF7;&#x6C42;&#x7684; SNI&#xFF09;
    # &#x5C06;&#x8FC7;&#x6EE4;&#x8FC7;&#x7684; SNI &#x5B58;&#x5165;&#x81EA;&#x5B9A;&#x4E49;&#x53D8;&#x91CF; $filtered_sni_name&#xFF0C;
    # &#x5982;&#x679C;&#x6CA1;&#x6709;&#x5339;&#x914D;&#x5230;&#x4EFB;&#x4F55;&#x89C4;&#x5219;&#xFF0C;&#x5219;&#x9ED8;&#x8BA4;&#x4E3A;&#x4E00;&#x4E2A; 443 &#x7AEF;&#x53E3;&#x6CA1;&#x6709;&#x670D;&#x52A1;&#x7684;&#x672C;&#x5730;&#x56DE;&#x73AF;&#x3002;
    map $ssl_preread_server_name $filtered_sni_name {
        ~^(.*|)netflix\.com$ $ssl_preread_server_name;
        ~^(.*|)netflix\.net$ $ssl_preread_server_name;
        ~^(.*|)nflximg\.com$ $ssl_preread_server_name;
        ~^(.*|)nflximg\.net$ $ssl_preread_server_name;
        ~^(.*|)nflxvideo\.net$ $ssl_preread_server_name;
        ~^(.*|)nflxext\.com$ $ssl_preread_server_name;
        ~^(.*|)nflxso\.net$ $ssl_preread_server_name;
        ~^(.*|)fast\.com$ $ssl_preread_server_name;
        default &quot;127.255.255.255&quot;;
    }

    server {
        resolver 1.1.1.1;
        listen 443;
        ssl_preread on;
        # &#x53EA;&#x4EE3;&#x7406;&#x7ECF;&#x8FC7; SNI &#x8FC7;&#x6EE4;&#x7684;&#x6D41;&#x91CF;&#xFF0C;
        # &#x5176;&#x4ED6;&#x6D41;&#x91CF;&#x90FD;&#x88AB;&#x4E22;&#x5165;&#x4E0D;&#x5B58;&#x5728;&#x7684;&#x5730;&#x5740;
        proxy_pass $filtered_sni_name:443;
    }
}
</code></pre>
<p>&#x4FDD;&#x5B58;&#x5B8C;&#x4E86;&#x91CD;&#x8F7D; Nginx &#x5373;&#x53EF;&#x3002;&#x8FD9;&#x91CC;&#x52A0; map &#x6765;&#x8FC7;&#x6EE4;&#x6D41;&#x91CF;&#x7684;&#x610F;&#x4E49;&#x5728;&#x4E8E;&#x9632;&#x6B62;&#x670D;&#x52A1;&#x5668;&#x88AB;&#x6EE5;&#x7528;&#x6210;&#x4EFB;&#x610F;&#x7F51;&#x7AD9;&#x7684;&#x53CD;&#x4EE3;&#x3002;</p>
<h3 id="2-dnsmasq">2. Dnsmasq</h3>
<p>&#x5982;&#x679C;&#x5230;&#x8FD9;&#x91CC;&#x4F60;&#x5DF2;&#x7ECF;&#x660E;&#x767D;&#x662F;&#x600E;&#x4E48;&#x4E00;&#x56DE;&#x4E8B;&#x7684;&#x8BDD;&#xFF0C;&#x5927;&#x6982;&#x4E0D;&#x9700;&#x8981; DNS &#x4F60;&#x4E5F;&#x77E5;&#x9053;&#x600E;&#x4E48;&#x5F04;&#x4E86;&#x2500;&#x2500; hosts &#x6587;&#x4EF6;&#x5199;&#x5165;&#x4E0A;&#x8FB9;&#x8FD9;&#x4E9B;&#x57DF;&#x540D;&#xFF0C;&#x6307;&#x5411;&#x90A3;&#x4E2A;&#x670D;&#x52A1;&#x5668;&#x7684; IP &#x5C31;&#x884C;&#x3002;&#x5F53;&#x7136;&#x4E3A;&#x4E86;&#x65B9;&#x4FBF;&#x4F7F;&#x7528;&#x6211;&#x4EEC;&#x7EE7;&#x7EED;&#xFF0C;&#x5047;&#x5B9A;&#x4E0A;&#x8FB9;&#x6CA1;&#x88AB; Netflix &#x5C01;&#x9501;&#x7684; IP &#x662F; <code>a.b.c.d</code>&#xFF0C;&#x73B0;&#x5728;&#x4EFB;&#x610F;&#x627E;&#x4E00;&#x4E2A;&#x5EF6;&#x8FDF;&#x8F83;&#x4F4E;&#x7684;&#x516C;&#x7F51;&#x670D;&#x52A1;&#x5668;&#xFF0C;&#x6216;&#x8005;&#x7EE7;&#x7EED;&#x7528; <code>a.b.c.d</code> &#x4E5F;&#x53EF;&#x4EE5;&#x3002;&#x5047;&#x5B9A;&#x8FD9;&#x6B21;&#x7684; IP &#x53EB; <code>1.2.3.4</code>&#x3002;&#x5728; <code>1.2.3.4</code> &#x4E0A;&#x7684; Dnsmasq &#x589E;&#x52A0;&#x914D;&#x7F6E;&#x5982;&#x4E0B;&#xFF1A;</p>
<pre><code>address=/netflix.com/a.b.c.d
address=/netflix.net/a.b.c.d
address=/nflxvideo.net/a.b.c.d
address=/nflxext.com/a.b.c.d
address=/nflximg.com/a.b.c.d
address=/nflximg.net/a.b.c.d
address=/nflxso.net/a.b.c.d
address=/fast.com/a.b.c.d
</code></pre>
<p>&#x4FDD;&#x5B58;&#x540E;&#x91CD;&#x542F; Dnsmasq &#x5373;&#x53EF;&#x3002;</p>
<h3 id="3-%E4%BD%BF%E7%94%A8">3. &#x4F7F;&#x7528;</h3>
<p>&#x73B0;&#x5728;&#x544A;&#x8BC9;&#x4F60;&#x7684;&#x670B;&#x53CB;&#xFF0C;&#x8BA9;&#x4ED6;&#x628A;&#x7F51;&#x7EDC;&#x8BBE;&#x7F6E;&#x7684; DNS &#x6539;&#x6210; <code>1.2.3.4</code> &#x5C31;&#x53EF;&#x4EE5;&#x4F7F;&#x7528; Netflix &#x54AF;&#xFF0C;&#x7535;&#x89C6;&#x673A; PlayStation Xbox &#x5565;&#x7684;&#x4E5F;&#x662F;&#x6539;&#x4E2A; DNS &#x5C31;&#x80FD;&#x7528;&#x3002;&#x53EA;&#x8981;&#x4F7F;&#x7528;&#x8005;&#x53EF;&#x4EE5;&#x6B63;&#x5E38;&#x8FDE;&#x63A5; <code>a.b.c.d</code> &#x548C; <code>1.2.3.4</code> &#x5373;&#x53EF;&#x3002;&#x5982;&#x679C;&#x4F7F;&#x7528;&#x8005;&#x8FDE;&#x63A5;&#x4E0D;&#x5230;&#x8FD9;&#x4FE9;&#x5730;&#x5740;&#x7684;&#x8BDD;&#x1F914;&#x90A3;&#x5F97;&#x914D;&#x5408;&#x4E2D;&#x8F6C;&#x4EE3;&#x7406;&#x66F4;&#x590D;&#x6742;&#x7684;&#x73A9;&#x6CD5;&#xFF0C;&#x7559;&#x5230;&#x4EE5;&#x540E;&#x518D;&#x5199;&#x5427;&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[SFTPGo 自建 Keepass 同步服务]]></title><description><![CDATA[跨设备同步一直是我使用 Keepass 的一个巨大痛点。本篇随便谈谈用 SFTPGo 这个工具自建云盘云同步平台，给 Keepass 或者任何类似的软件提供云同步服务。]]></description><link>https://xcel.me/keepass-sync-with-sftpgo/</link><guid isPermaLink="false">5fcb3bc1cde50f60c3c689e9</guid><category><![CDATA[Sync]]></category><category><![CDATA[Server]]></category><category><![CDATA[Keepass]]></category><category><![CDATA[Docker]]></category><category><![CDATA[Nginx]]></category><category><![CDATA[Self host]]></category><category><![CDATA[OpenSource]]></category><category><![CDATA[SysAdmin]]></category><category><![CDATA[网盘]]></category><category><![CDATA[同步盘]]></category><category><![CDATA[自建]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Sat, 05 Dec 2020 12:36:59 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>&#x8DE8;&#x8BBE;&#x5907;&#x540C;&#x6B65;&#x4E00;&#x76F4;&#x662F;&#x6211;&#x4F7F;&#x7528; Keepass &#x7684;&#x4E00;&#x4E2A;&#x5DE8;&#x5927;&#x75DB;&#x70B9;&#x3002;Keepass &#x672C;&#x8EAB;&#x4F5C;&#x4E3A;&#x5F00;&#x6E90;&#x8F6F;&#x4EF6;&#xFF0C;&#x6570;&#x636E;&#x5E93;&#x6709;&#x5F00;&#x653E;&#x6807;&#x51C6;&#xFF0C;&#x6D41;&#x884C;&#x7684;&#x5404;&#x5BB6;&#x5B9E;&#x73B0;&#x90FD;&#x80FD;&#x901A;&#x7528;&#xFF0C;&#x4F46;&#x5B83;&#x5E76;&#x6CA1;&#x6709;&#x63D0;&#x4F9B;&#x4EFB;&#x4F55;&#x4E91;&#x540C;&#x6B65;&#x670D;&#x52A1;&#xFF0C;&#x4E5F;&#x6CA1;&#x6709;&#x63D0;&#x4F9B;&#x7EDF;&#x4E00;&#x7684;&#x4E91;&#x540C;&#x6B65;&#x6807;&#x51C6;&#x3002;&#x8FD9;&#x5C31;&#x76F8;&#x5F53;&#x75DB;&#x82E6;&#x4E86;&#xFF0C;&#x7B49;&#x4E8E;&#x6BCF;&#x6B21;&#x66F4;&#x6539;&#x90FD;&#x5F97;&#x624B;&#x52A8;&#x540C;&#x6B65;&#xFF0C;&#x8DE8;&#x8BBE;&#x5907;&#x7684;&#x65F6;&#x5019;&#x5C24;&#x5176;&#x9EBB;&#x70E6;&#x3002;&#x672C;&#x7BC7;&#x968F;&#x4FBF;&#x8C08;&#x8C08;&#x7528; SFTPGo &#x8FD9;&#x4E2A;&#x5DE5;&#x5177;&#x81EA;&#x5EFA;&#x4E91;&#x76D8;&#x4E91;&#x540C;&#x6B65;&#x5E73;&#x53F0;&#xFF0C;&#x7ED9; Keepass &#x6216;&#x8005;&#x4EFB;&#x4F55;&#x7C7B;&#x4F3C;&#x7684;&#x8F6F;&#x4EF6;&#x63D0;&#x4F9B;&#x4E91;&#x540C;&#x6B65;&#x670D;&#x52A1;&#x3002;</p>
<p>&#x5F53;&#x7136;&#xFF0C;Onedrive Gdrive &#x8FD9;&#x4E9B;&#x670D;&#x52A1;&#x5982;&#x679C;&#x80FD;&#x5904;&#x7406;&#x597D;&#x4EE3;&#x7406;&#x95EE;&#x9898;&#x3001;&#x76EE;&#x5F55;&#x6302;&#x8F7D;&#x95EE;&#x9898;&#x7B49;&#x7B49;&#x8FD8;&#x662F;&#x5F88;&#x597D;&#x7528;&#x7684;&#xFF0C;&#x5E76;&#x4E14;&#x5728;&#x5B58;&#x50A8;&#x65B9;&#x9762;&#x663E;&#x7136;&#x6BD4;&#x81EA;&#x5EFA;&#x66F4;&#x53EF;&#x9760;&#x3002;&#x4E0D;&#x8FC7;&#x672C;&#x7740;&#x6298;&#x817E;&#x7684;&#x7CBE;&#x795E;&#xFF0C;&#x54B1;&#x8FD8;&#x662F;&#x8981;&#x73A9;&#x800D;&#x4E00;&#x628A;&#x81EA;&#x5EFA;&#x670D;&#x52A1;&#x3002;</p>
<p>SFTPGo <a href="https://github.com/drakkan/sftpgo">GitHub &#x9879;&#x76EE;&#x5730;&#x5740;&#x70B9;&#x6B64;</a>&#x3002;&#x4E3B;&#x6D41;&#x7684;&#x6587;&#x4EF6;&#x4F20;&#x8F93;&#x3001;&#x540C;&#x6B65;&#x534F;&#x8BAE;&#x5B83;&#x51E0;&#x4E4E;&#x90FD;&#x652F;&#x6301;&#x4E86;&#xFF0C;&#x603B;&#x6709;&#x4E00;&#x6B3E;&#x9002;&#x5408;&#x4F60;&#x7684;&#x7CFB;&#x7EDF;&#xFF0C;&#x5305;&#x62EC;&#x5E76;&#x4E14;&#x4E0D;&#x9650;&#x4E8E; FTP&#xFF0C;FTPS&#xFF0C;SFTP&#xFF0C;WebDAV&#xFF0C;SCP&#xFF0C;Git&#x2026;&#x2026; &#x8FD8;&#x652F;&#x6301; S3&#xFF0C;GCP&#xFF0C;Azure &#x7B49;&#x670D;&#x52A1;&#x4F5C;&#x4E3A;&#x5B58;&#x50A8;&#x540E;&#x7AEF;&#xFF0C;&#x4FDD;&#x8BC1;&#x6570;&#x636E;&#x5B89;&#x5168;&#x3002;&#x5E76;&#x4E14;&#x8FD8;&#x63D0;&#x4F9B;&#x4E86;&#x5B8C;&#x6574;&#x7684; RESTful API&#x3001;&#x666E;&#x9C81;&#x7C73;&#x4FEE;&#x65AF;&#x76D1;&#x63A7;&#x6307;&#x6807;&#x3001;&#x7075;&#x6D3B;&#x7684;&#x7528;&#x6237;&#x9274;&#x6743;&#x65B9;&#x5F0F;&#x3001;&#x7528;&#x6237;&#x5404;&#x81EA;&#x76EE;&#x5F55;&#x6743;&#x9650;&#x914D;&#x7F6E;&#x3001;&#x5BB9;&#x91CF;&#x6309;&#x9700;&#x5206;&#x914D;&#x6309;&#x9700;&#x9650;&#x5236;&#x3001;&#x4E0A;&#x4F20;&#x4E0B;&#x8F7D;&#x901F;&#x5EA6;&#x9650;&#x5236;&#x3001;&#x5BA2;&#x6237;&#x7AEF;&#x6570;&#x9650;&#x5236;&#x3001;&#x767B;&#x5F55; IP &#x9650;&#x5236;&#x3001;&#x6587;&#x4EF6;&#x53D8;&#x66F4;&#x63A8;&#x9001;&#x901A;&#x77E5;&#x2026;&#x2026; &#x55EF;&#xFF0C;&#x7B80;&#x800C;&#x8A00;&#x4E4B;&#x4F60;&#x751A;&#x81F3;&#x53EF;&#x4EE5;&#x7528;&#x5B83;&#x5F00;&#x5356;&#x4E91;&#x76D8;&#x4E86;&#x1F923;&#x3002;</p>
<h2 id="sftpgo">&#x90E8;&#x7F72; SFTPGo</h2>
<p>&#x7531;&#x4E8E;&#x7279;&#x6027;&#x6BD4;&#x8F83;&#x591A;&#xFF0C;&#x672C;&#x5730;&#x5B89;&#x88C5;&#x914D;&#x7F6E;&#x8D77;&#x6765;&#x4E5F;&#x6BD4;&#x8F83;&#x7E41;&#x7410;&#xFF0C;&#x8FD9;&#x91CC;&#x54B1;&#x5C31;&#x7528;&#x5BB9;&#x5668;&#x90E8;&#x7F72;&#x4E86;&#x3002;</p>
<pre><code class="language-shell"># &#x4ECE; GitHub &#x6E90;&#x62C9;&#x53D6;&#x955C;&#x50CF;
docker pull ghcr.io/drakkan/sftpgo:edge
</code></pre>
<p>&#x8FD9;&#x91CC;&#x6211;&#x9009;&#x62E9;&#x90E8;&#x7F72; WebDAV&#x3001;FTP &#x4E24;&#x79CD;&#x534F;&#x8BAE;&#xFF0C;&#x54B1;&#x4EEC;&#x7684;&#x5BB9;&#x5668;&#x540D;&#x79F0;&#x53EB; <code>sftpgo-server</code>&#xFF0C;&#x914D;&#x7F6E;&#x4E3A; <code>--restart always</code> &#x8BA9;&#x5B83;&#x968F; docker &#x670D;&#x52A1;&#x4E00;&#x540C;&#x542F;&#x52A8;:</p>
<pre><code class="language-shell">docker run --name &quot;sftpgo-server&quot; \
-p 127.0.0.1:10125:8080 \
-p 127.0.0.1:10126:8081 \
-p 2022:2022 \
-p 21:21 \
-p 20:20 \
-p 50000-50100:50000-50100 \
-e SFTPGO_FTPD__BIND_PORT=21 \
-e SFTPGO_WEBDAVD__BIND_PORT=8081 \
-e SFTPGO_SFTPD__TLS_MODE=0 \
--mount type=bind,source=/home/ftp,target=/srv/sftpgo \
--mount type=bind,source=/home/ftp,target=/var/lib/sftpgo \
-d --restart always \
ghcr.io/drakkan/sftpgo:edge
</code></pre>
<ul>
<li><code>8080</code> &#x4E3A;&#x5B83;&#x7684;&#x7BA1;&#x7406;&#x5458; web &#x914D;&#x7F6E;&#x9875;&#x9762;&#xFF0C;&#x54B1;&#x6620;&#x5C04;&#x5230;&#x672C;&#x5730; <code>10125</code>&#xFF1B;</li>
<li>&#x5176;&#x4E2D; WebDAV &#x914D;&#x7F6E;&#x4E3A;&#x5BB9;&#x5668;&#x4E2D; <code>8081</code> &#x7AEF;&#x53E3;&#xFF0C;&#x6620;&#x5C04;&#x5230;&#x672C;&#x5730; <code>10126</code> &#x7AEF;&#x53E3;&#xFF1B;</li>
<li>FTP &#x914D;&#x7F6E;&#x4E3A;&#x5BB9;&#x5668;&#x4E2D; <code>21</code> &#x548C; <code>20</code> &#x7AEF;&#x53E3;&#xFF0C;&#x88AB;&#x52A8;&#x6A21;&#x5F0F;&#x4E3A;&#x5BB9;&#x5668;&#x4E0B; <code>50000-50100</code> &#x7AEF;&#x53E3;&#xFF0C;&#x5206;&#x522B;&#x6620;&#x5C04;&#x5230;&#x672C;&#x5730; <code>21</code>&#xFF0C;<code>20</code> &#x548C; <code>50000-50100</code> &#x7AEF;&#x53E3;&#xFF1B;</li>
<li><code>/srv/sftpgo</code> &#x4E3A;&#x5BB9;&#x5668;&#x4E2D;&#x5B58;&#x653E;&#x7528;&#x6237;&#x6570;&#x636E;&#x7684;&#x76EE;&#x5F55;&#xFF0C;&#x6302;&#x8F7D;&#x5230;&#x672C;&#x5730; <code>/home/ftp</code> &#x76EE;&#x5F55;&#xFF1B;</li>
<li><code>/var/lib/sftpgo</code> &#x4E3A;&#x5BB9;&#x5668;&#x4E2D;&#x914D;&#x7F6E;&#x6570;&#x636E;&#x7684;&#x76EE;&#x5F55;&#xFF0C;&#x6302;&#x8F7D;&#x5230;&#x672C;&#x5730; <code>/home/ftp</code> &#x76EE;&#x5F55;&#xFF1B;</li>
</ul>
<p>&#x5982;&#x679C;&#x6709;&#x78B0;&#x5230; docker &#x7684; root &#x6743;&#x9650;&#x95EE;&#x9898;&#x7ED1;&#x4E0D;&#x4E0A;&#x7AEF;&#x53E3;&#xFF0C;&#x53EF;&#x4EE5;&#x8BD5;&#x7740;&#x52A0;&#x4E0A;&#x8FD9;&#x4E9B;&#x9009;&#x9879;</p>
<pre><code>--cap-add NET_BIND_SERVICE --sysctl net.ipv4.ip_unprivileged_port_start=0
</code></pre>
<p>&#x7136;&#x540E;&#x54B1;&#x628A; WebDAV &#x670D;&#x52A1;&#x8DDF;&#x7BA1;&#x7406;&#x5458; web &#x914D;&#x7F6E;&#x9875;&#x9762;&#x53CD;&#x4EE3;&#x5230;&#x672C;&#x7AD9;&#x5BF9;&#x5E94; URL &#x4E0A;&#xFF08;&#x1F602;&#x5730;&#x5740;&#x54B1;&#x5C31;&#x4E0D;&#x544A;&#x8BC9;&#x4F60;&#x4E86;&#x3002;</p>
<h2 id>&#x7528;&#x6237;&#x7BA1;&#x7406;</h2>
<p>&#x8FDB;&#x5165; web &#x7BA1;&#x7406;&#x5458;&#x9875;&#x9762;&#xFF0C;&#x6DFB;&#x52A0;&#x597D;&#x7528;&#x6237;&#x3001;&#x5BC6;&#x7801;&#x5BC6;&#x94A5;&#x7B49;&#xFF0C;&#x5C31;&#x53EF;&#x4EE5;&#x4F7F;&#x7528;&#x4E86;&#xFF01;&#x6CE8;&#x610F;&#x81EA;&#x5E26;&#x7684; web &#x7BA1;&#x7406;&#x5458;&#x9875;&#x9762;&#x5E76;&#x6CA1;&#x6709;&#x9274;&#x6743;&#xFF0C;&#x4E0D;&#x8981;&#x66B4;&#x9732;&#x5230;&#x516C;&#x7F51;&#x4E0A;&#x54E6;&#xFF01;&#x53EF;&#x4EE5;&#x81EA;&#x5DF1; ssh &#x53CD;&#x4EE3;&#x5230;&#x672C;&#x5730;&#x7535;&#x8111;&#x4E0A;&#x6765;&#x8BBF;&#x95EE;&#xFF0C;&#x6309;&#x672C;&#x6587;&#x7684;&#x914D;&#x7F6E;&#x5C31;&#x53EF;&#x4EE5;&#x50CF;&#x8FD9;&#x6837;</p>
<pre><code>ssh -L 8080:127.0.0.1:10125 &lt;&#x670D;&#x52A1;&#x5668;&gt;
</code></pre>
<p>&#x7136;&#x540E;&#x81EA;&#x5DF1;&#x7535;&#x8111;&#x4E0A;&#x6D4F;&#x89C8;&#x5668; <code>http://localhost:8080</code> &#x5C31;&#x53EF;&#x4EE5;&#x6253;&#x5F00;&#x7BA1;&#x7406;&#x9875;&#x4E86;&#x3002;</p>
<h2 id>&#x4F7F;&#x7528;</h2>
<p>Windows 10&#x3001;macOS &#x90FD;&#x652F;&#x6301; webdav &#x6302;&#x8F7D;&#xFF0C;&#x5B8C;&#x4E86;&#x628A; Keepass &#x7684;&#x6570;&#x636E;&#x5E93;&#x4E22;&#x5BF9;&#x5E94;&#x76EE;&#x5F55;&#x5C31;&#x884C;&#x4E86;&#xFF0C;&#x53EA;&#x8981;&#x8F6F;&#x4EF6;&#x6539;&#x52A8;&#x4FDD;&#x5B58;&#x4E86;&#x6570;&#x636E;&#x5E93;&#x5C31;&#x81EA;&#x52A8;&#x540C;&#x6B65;&#x4E86;&#x3002;Linux &#x4E0A;&#x9009;&#x62E9;&#x5C31;&#x66F4;&#x7075;&#x6D3B;&#x4E86;&#xFF0C;&#x968F;&#x4F60;&#x7528;&#x5565;&#x6302;&#x8F7D;&#x3002;&#x79FB;&#x52A8;&#x8BBE;&#x5907;&#x4E0A;&#xFF0C;Android &#x7684; Keepass2android &#x672C;&#x8EAB;&#x5C31;&#x652F;&#x6301;&#x5404;&#x79CD;&#x534F;&#x8BAE;&#xFF0C;&#x54B1;&#x521A;&#x7528; webdav &#x540C;&#x6B65;&#x4E0A;&#x53BB;&#x7684;&#x6570;&#x636E;&#x5E93;&#x8F7B;&#x677E;&#x6253;&#x5F00;&#xFF1B;iOS &#x54B1;&#x4E0D;&#x662F;&#x7528;&#x6237;&#x6682;&#x4E0D;&#x6E05;&#x695A;&#x3002;</p>
<h2 id>&#x5176;&#x4ED6;</h2>
<p>&#x7531;&#x4E8E;&#x662F;&#x81EA;&#x5EFA;&#x670D;&#x52A1;&#xFF0C;&#x6240;&#x4EE5;&#x7075;&#x6D3B;&#x6027;&#x5F88;&#x5927;&#xFF0C;&#x914D;&#x5408;&#x5176;&#x4ED6;&#x8F6F;&#x4EF6;&#x53EF;&#x4EE5;&#x5F88;&#x8F7B;&#x677E;&#x5B9E;&#x73B0;&#x6587;&#x4EF6;&#x5171;&#x4EAB;&#x3002;&#x76EE;&#x524D;&#x54B1;&#x5728;&#x672C;&#x7AD9;&#x4E0A;&#x6709;&#x653E;&#x4E86; Nginx &#x516C;&#x5F00;&#x76EE;&#x5F55;&#xFF0C;&#x7ED9;&#x540C;&#x5B66;&#x540C;&#x4E8B;&#x4F20;&#x6587;&#x4EF6;&#x5F88;&#x65B9;&#x4FBF;&#xFF0C;&#x4E0D;&#x9700;&#x8981;&#x767B;&#x5F55;&#x4E0D;&#x9700;&#x8981;&#x4F1A;&#x5458;&#xFF0C;&#x4E00;&#x4E2A;&#x5730;&#x5740;&#x7ED9;&#x5230;&#x6D4F;&#x89C8;&#x5668;&#x6253;&#x5F00;&#x5C31; ok&#xFF0C;&#x7528;&#x4E0D;&#x7740;&#x8FA3;&#x9E21;&#x5FAE;&#x4FE1;&#x8DDF;&#x767E;&#x5EA6;&#x76D8;&#xFF0C;&#x6216;&#x8005;&#x53BB;&#x6253;&#x5370;&#x5E97;&#x4E5F;&#x4E0D;&#x7528;&#x5E26; U &#x76D8;&#x653E;&#x6587;&#x6863;&#x6216;&#x8005;&#x767B;&#x5F55;&#x5565;&#xFF0C;&#x4ECE;&#x81EA;&#x5DF1;&#x7684;&#x7F51;&#x76D8;&#x4E0A;&#x62FF;&#x5C31;&#x662F;&#x4E86;&#x2026;&#x2026;</p>
<p>&#x5F53;&#x7136;&#xFF0C;&#x53D7;&#x9650;&#x4E8E;&#x81EA;&#x5EFA;&#x670D;&#x52A1;&#xFF0C;&#x4F20;&#x8F93;&#x901F;&#x5EA6;&#x53EF;&#x80FD;&#x4E0D;&#x592A;&#x7406;&#x60F3;&#xFF0C;&#x4E5F;&#x653E;&#x4E0D;&#x4E86;&#x592A;&#x5927;&#x7684;&#x4E1C;&#x897F;&#xFF0C;&#x4F46;&#x662F;&#x540C;&#x6B65;&#x6587;&#x6863;&#x7EDD;&#x5BF9; ok&#xFF0C;&#x4EE5;&#x540E;&#x6211;&#x751A;&#x81F3;&#x8FD8;&#x6253;&#x7B97;&#x66FF;&#x6362;&#x6389;&#x5728;&#x7528;&#x7684;&#x575A;&#x679C;&#x4E91;&#xFF0C;&#x81EA;&#x5EFA; webdav &#x670D;&#x52A1;&#x672C;&#x8EAB;&#x8DE8;&#x5E73;&#x53F0;&#x517C;&#x5BB9;&#x6027;&#x5C31;&#x5F88;&#x597D;&#xFF0C;&#x4E0D;&#x9700;&#x8981;&#x989D;&#x5916;&#x5BA2;&#x6237;&#x7AEF;&#xFF0C;&#x4E5F;&#x6CA1;&#x6709;&#x6D41;&#x91CF;&#x9650;&#x5236;&#x3002;&#x4E0D;&#x8FC7;&#x672C;&#x7BC7;&#x4E2D;&#x7684;&#x6570;&#x636E;&#x5B58;&#x50A8;&#x662F;&#x90E8;&#x7F72;&#x670D;&#x52A1;&#x5668;&#x786C;&#x76D8;&#x4E0A;&#x7684;&#xFF0C;&#x867D;&#x8BF4;&#x6BD4;&#x4E2A;&#x4EBA;&#x624B;&#x673A;&#x7535;&#x8111;&#x53EF;&#x9760;&#x591A;&#x4E86;&#xFF0C;&#x4F46;&#x4ECD;&#x6709;&#x6570;&#x636E;&#x4E22;&#x5931;&#x4E4B;&#x865E;&#xFF0C;&#x4FDD;&#x9669;&#x8D77;&#x89C1;&#xFF0C;&#x6709;&#x91CD;&#x8981;&#x6587;&#x4EF6;&#x7684;&#x8BDD;&#x8FD8;&#x662F;&#x5EFA;&#x8BAE;&#x9009;&#x62E9;&#x5927;&#x5382;&#x4E91;&#x670D;&#x52A1;&#x505A;&#x5B58;&#x50A8;&#x540E;&#x7AEF;&#xFF0C;&#x50CF; S3&#x3001;GCP &#x8DDF; Azure &#x8FD9;&#x4E9B; SFTPGo &#x672C;&#x8EAB;&#x5C31;&#x652F;&#x6301;&#xFF0C;&#x6216;&#x8005;&#x4E5F;&#x53EF;&#x4EE5;&#x7528; rsync &#x53BB;&#x6302;&#x8F7D;&#x5176;&#x4ED6;&#x4E91;&#x670D;&#x52A1;&#x4F5C;&#x5B58;&#x50A8;&#x540E;&#x7AEF;&#x3002;</p>
<hr>
<h2 id>&#x66F4;&#x65B0;</h2>
<ul>
<li>2020/12/6 SFTPGo &#x5728; Windows 10 explorer &#x4E2D;&#x4E0A;&#x4F20;&#x6587;&#x4EF6;&#x53D8;&#x7A7A;&#x7684;&#x95EE;&#x9898;&#x5DF2;&#x89E3;&#x51B3;&#xFF1B;</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Calibre 自建私人在线图书馆]]></title><description><![CDATA[自建云端电子书库，在任何设备上随时随地云阅读]]></description><link>https://xcel.me/calibre-personal-online-library/</link><guid isPermaLink="false">5e82bf79e6c9bc14ba2b54f0</guid><category><![CDATA[Calibre]]></category><category><![CDATA[Ebooks]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Server]]></category><category><![CDATA[Nginx]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Thu, 02 Apr 2020 02:31:53 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Calibre &#x662F;&#x4E00;&#x4F4D;&#x5370;&#x5EA6;&#x5927;&#x4F6C;&#x7F16;&#x5199;&#x5E76;&#x7EF4;&#x62A4;&#x7684;&#x7535;&#x5B50;&#x4E66;&#x7BA1;&#x7406;&#x8F6F;&#x4EF6;&#xFF0C;&#x96C6;&#x6210;&#x4E86;&#x7535;&#x5B50;&#x4E66;&#x7BA1;&#x7406;&#x3001;&#x5206;&#x7C7B;&#x3001;&#x7F16;&#x8F91;&#x3001;&#x8F6C;&#x6362;&#x3001;&#x641C;&#x7D22;&#x7B49;&#x7B49;&#x6240;&#x6709;&#x80FD;&#x60F3;&#x5230;&#x7684;&#x529F;&#x80FD;&#xFF0C;kindle &#x7528;&#x6237;&#x80AF;&#x5B9A;&#x5F88;&#x719F;&#x6089;&#x5566;&#x3002;&#x4F60;&#x53EF;&#x80FD;&#x4E0D;&#x77E5;&#x9053;&#x7684;&#x662F;&#xFF0C;&#x5B83;&#x8FD8;&#x6709;&#x4E2A;&#x5185;&#x5BB9;&#x670D;&#x52A1;&#x5668;&#x529F;&#x80FD;&#xFF0C;&#x5982;&#x679C;&#x80FD;&#x5229;&#x7528;&#x8D77;&#x6765;&#x4F60;&#x53EF;&#x80FD;&#x5C31;&#x4E0D;&#x9700;&#x8981;&#x4EC0;&#x4E48; kindle &#x4E86;&#xFF0C;&#x53EA;&#x9700;&#x8981;&#x6709;&#x4E00;&#x4E2A;&#x6D4F;&#x89C8;&#x5668;&#x5C31;&#x80FD;&#x968F;&#x65F6;&#x9605;&#x8BFB;&#x5E76;&#x4E14;&#x540C;&#x6B65;&#x8FDB;&#x5EA6;&#xFF0C;&#x628A;&#x4EFB;&#x4F55;&#x624B;&#x673A;&#x5E73;&#x677F;&#x7B14;&#x8BB0;&#x672C;&#x53D8;&#x6210;&#x4E91;&#x7AEF;&#x9605;&#x8BFB;&#x5668;&#xFF01;</p>
<p>&#x867D;&#x7136; Calibre &#x5185;&#x5BB9;&#x670D;&#x52A1;&#x5668;&#x7684;&#x754C;&#x9762;&#x76EE;&#x524D;&#x8FD8;&#x6BD4;&#x8F83;&#x7B80;&#x964B;&#xFF0C;&#x4F46;&#x5DF2;&#x7ECF;&#x5230;&#x4E86;&#x76F8;&#x5F53;&#x582A;&#x7528;&#x7684;&#x7A0B;&#x5EA6;&#xFF0C;&#x7528;&#x6765;&#x81EA;&#x5EFA;&#x4E2A;&#x4EBA;&#x5728;&#x7EBF;&#x56FE;&#x4E66;&#x670D;&#x52A1;&#x518D;&#x597D;&#x4E0D;&#x8FC7;&#x3002;&#x5982;&#x679C;&#x4F60;&#x6709;&#x4F7F;&#x7528; NAS&#xFF08;&#x6216;&#x8005;&#x4E91;&#x670D;&#x52A1;&#x5668;&#x66F4;&#x597D;&#xFF09;&#xFF0C;&#x5E76;&#x4E14;&#x6709;&#x9605;&#x8BFB;&#x7535;&#x5B50;&#x4E66;&#x7684;&#x9700;&#x6C42;&#xFF0C;&#x4E00;&#x5B9A;&#x8981;&#x5B89;&#x88C5;&#x8BD5;&#x7528;&#x4E0B; Calibre &#x7684;&#x5185;&#x5BB9;&#x670D;&#x52A1;&#x5668;&#x54E6;&#x3002;</p>
<p>&#x8FD9;&#x5468;&#x6211;&#x5728;&#x672C;&#x7AD9;&#x4E0A;&#x642D;&#x5EFA;&#x4E86;&#x4E00;&#x4E2A;&#xFF0C;&#x76EE;&#x524D;&#x4F7F;&#x7528;&#x4F53;&#x9A8C;&#x5F88;&#x4E0D;&#x9519;&#xFF0C;&#x6324;&#x5730;&#x94C1;&#x3001;&#x5403;&#x996D;&#x3001;&#x65E0;&#x804A;&#x7684;&#x65F6;&#x5019;&#x968F;&#x65F6;&#x62BD;&#x51FA;&#x624B;&#x673A;&#x6253;&#x5F00; <a href="https://books.xcel.me">books.xcel.me</a> &#x5C31;&#x80FD;&#x7EE7;&#x7EED;&#x9605;&#x8BFB;&#xFF0C;&#x6D4F;&#x89C8;&#x5668;&#x4E91;&#x7AEF;&#x9605;&#x8BFB;&#x7684;&#x4FBF;&#x643A;&#x6027;&#x662F;&#x53EF;&#x4EE5;&#x5339;&#x654C;&#x5168;&#x5E73;&#x53F0;&#x7684; kindle &#x7684;&#x3002;&#x4E0B;&#x9762;&#x7B80;&#x8981;&#x63D0;&#x4E0B;&#x5728;&#x672C;&#x7AD9; ubuntu 16.04 &#x4E0A;&#x7684;&#x642D;&#x5EFA;&#x6D41;&#x7A0B;&#x3002;</p>
<h1 id="0x0calibre">0x0 &#x5B89;&#x88C5; Calibre</h1>
<p>Calibre &#x7684;&#x65E0;&#x5934;&#x670D;&#x52A1;&#x5668;&#x548C;&#x684C;&#x9762;&#x7248;&#x7528;&#x7684;&#x662F;&#x540C;&#x4E00;&#x4EFD;&#x4EE3;&#x7801;&#x3002;&#x5982;&#x679C;&#x4F60;&#x7CFB;&#x7EDF;&#x662F; archlinux&#xFF0C;<code>pacman -S calibre</code> &#x5C31;&#x80FD;&#x5B89;&#x88C5;&#x6700;&#x65B0;&#x7248;&#xFF0C;&#x65E0;&#x9700;&#x8003;&#x8651;&#x4F9D;&#x8D56;&#x95EE;&#x9898;&#xFF08;&#x6BD4; ubuntu &#x65B9;&#x4FBF;&#x591A;&#x4E86;&#x5462;&#xFF09;&#x3002;</p>
<p>&#x9274;&#x4E8E; ubuntu &#x63D0;&#x4F9B;&#x7684;&#x7248;&#x672C;&#x76F8;&#x5F53;&#x843D;&#x540E;&#xFF0C;&#x8FD9;&#x91CC;&#x5EFA;&#x8BAE;&#x4F7F;&#x7528; Calibre &#x5B98;&#x65B9;&#x63D0;&#x4F9B;&#x7684;&#x811A;&#x672C;&#x6765;&#x5B89;&#x88C5;&#x5347;&#x7EA7;&#xFF0C;&#x53CD;&#x800C;&#x4E0D;&#x5EFA;&#x8BAE;&#x4F7F;&#x7528; <code>apt</code> &#x5305;&#x7BA1;&#x7406;&#x5B89;&#x88C5;&#x3002;</p>
<pre><code class="language-shell">sudo -v &amp;&amp; wget -nv -O- https://download.calibre-ebook.com/linux-installer.sh | sudo sh /dev/stdin
</code></pre>
<h1 id="0x1">0x1 &#x5B89;&#x88C5;&#x5FC5;&#x8981;&#x7684;&#x4F9D;&#x8D56;</h1>
<p>&#x4F7F;&#x7528; archlinux &#x7684;&#x8BDD;&#x5C31;&#x53EF;&#x4EE5;&#x8DF3;&#x8FC7;&#x8FD9;&#x4E00;&#x6B65;&#x5566;&#x3002;&#x867D;&#x7136;&#x6211;&#x4EEC;&#x8FD9;&#x91CC;&#x53EA;&#x9700;&#x8981;&#x65E0;&#x5934; Calibre &#x5185;&#x5BB9;&#x670D;&#x52A1;&#xFF0C;&#x4F46;&#x5B83;&#x4F9D;&#x8D56;&#x7684; Qt &#x65E0;&#x5934;&#x5E93;&#x4ECD;&#x7136;&#x4F9D;&#x8D56;&#x4E8E; x11 &#x7684; libGL&#xFF0C;ubuntu &#x670D;&#x52A1;&#x5668;&#x9700;&#x8981; <code>apt</code> &#x5B89;&#x88C5;&#x4E00;&#x4E2A; <code>libgl1-mesa-glx</code>&#x3002;</p>
<h1 id="0x2">0x2 &#x914D;&#x7F6E;&#x56FE;&#x4E66;&#x9986;&#x76EE;&#x5F55;&#x3001;&#x7528;&#x6237;&#x6570;&#x636E;&#x5E93;</h1>
<p>&#x73B0;&#x5728;&#x53EF;&#x4EE5;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x56FE;&#x4E66;&#x5E93;&#x5566;&#xFF0C;&#x5982;&#x679C;&#x4F60;&#x672C;&#x5730;&#x5DF2;&#x7ECF;&#x6709;&#x7BA1;&#x7406;&#x597D;&#x7684;&#x56FE;&#x4E66;&#x9986;&#xFF0C;&#x53EF;&#x4EE5;&#x4E0A;&#x4F20;&#x6574;&#x4E2A;&#x76EE;&#x5F55;&#x3002;<br>
&#x50CF;&#x4E0B;&#x9762;&#x8FD9;&#x6837;&#x4F7F;&#x7528; <code>calibredb</code> &#x53EF;&#x4EE5;&#x521B;&#x5EFA;&#x591A;&#x4E2A;&#x4E0D;&#x540C;&#x7684;&#x56FE;&#x4E66;&#x9986;&#x76EE;&#x5F55;&#xFF0C;&#x8FD9;&#x91CC;&#x65B0;&#x5EFA;&#x4E00;&#x4E2A; <code>/opt/library</code> &#x76EE;&#x5F55;&#xFF1A;</p>
<pre><code class="language-shell">calibredb --with-library /opt/library list
</code></pre>
<p>&#x7136;&#x540E;&#x6267;&#x884C;&#x4E0B;&#x9762;&#x7684;&#x547D;&#x4EE4;&#xFF0C;&#x5E76;&#x9009;&#x62E9;&#x65B0;&#x5EFA;&#x4E00;&#x4E2A;&#x65B0;&#x7528;&#x6237;&#xFF0C;&#x521B;&#x5EFA;&#x4E00;&#x4E2A;&#x7528;&#x6237;&#x6570;&#x636E;&#x5E93; <code>users.db</code>&#x3002;&#x540E;&#x7EED;&#x9700;&#x8981;&#x4F7F;&#x7528;&#x7528;&#x6237;/&#x5BC6;&#x7801;&#x6765;&#x8BBF;&#x95EE;&#x5728;&#x7EBF;&#x56FE;&#x4E66;&#x9986;&#x3002;</p>
<pre><code class="language-shell">calibre-server --userdb /opt/calibre/users.db --manage-users
</code></pre>
<h1 id="0x3">0x3 &#x8BBE;&#x7F6E;&#x81EA;&#x52A8;&#x8FD0;&#x884C;</h1>
<p>&#x65B0;&#x5EFA; <code>/etc/systemd/system/calibre.service</code> &#x5982;&#x4E0B;&#xFF0C;&#x5E26;&#x4E0A;&#x521A;&#x521A;&#x65B0;&#x5EFA;&#x7684;&#x7528;&#x6237;&#x6570;&#x636E;&#x5E93;&#x548C;&#x56FE;&#x4E66;&#x9986;&#x76EE;&#x5F55;&#xFF1A;</p>
<pre><code>[Unit]
Description=calibre content server
After=network.target

[Service]
Type=simple
User=www-data
Group=www-data
ExecStart=/opt/calibre/calibre-server --listen-on 127.0.0.1 --port 8080 --userdb /opt/calibre/users.db --enable-auth --auth-mode=basic --ban-after=3 --ban-for=60 /opt/library

[Install]
WantedBy=multi-user.target
</code></pre>
<p>&#x4FDD;&#x5B58;&#x540E; <code>systemctl daemon-reload; systemctl enable calibre; systemctl start calibre</code>&#xFF0C;&#x8BBE;&#x7F6E;&#x5F00;&#x673A;&#x542F;&#x52A8;&#x540C;&#x65F6;&#x5F00;&#x59CB;&#x670D;&#x52A1;&#x3002;</p>
<p>&#x8FD9;&#x91CC;&#x6709;&#x51E0;&#x5904;&#x548C;&#x5B89;&#x5168;&#x6709;&#x5173;&#x7684;&#x8BBE;&#x7F6E;&#xFF1A;</p>
<ul>
<li>&#x672C;&#x7AD9;&#x7F51;&#x7EDC;&#x670D;&#x52A1;&#x90FD;&#x4F7F;&#x7528; <code>www-data</code> &#x7528;&#x6237;&#x8EAB;&#x4EFD;&#x6765;&#x8FD0;&#x884C;&#xFF0C;&#x907F;&#x514D;&#x4F7F;&#x7528; <code>root</code>&#xFF1B;</li>
<li>&#x7F51;&#x7EDC;&#x670D;&#x52A1;&#x53EA;&#x76D1;&#x542C;&#x672C;&#x5730;&#x56DE;&#x73AF; <code>127.0.0.1</code> &#x6216;&#x8005; unixsocket&#xFF0C;&#x516C;&#x7F51;&#x7531; Nginx &#x53CD;&#x4EE3;&#xFF0C;&#x8FD9;&#x6837;&#x6240;&#x6709;&#x670D;&#x52A1;&#x7684;&#x5B89;&#x5168;&#x95EE;&#x9898;&#x3001;&#x6D41;&#x91CF;&#x9650;&#x5236;&#x3001;&#x7F13;&#x5B58;&#x8BBE;&#x7F6E;&#x7B49;&#x90FD;&#x80FD;&#x65B9;&#x4FBF;&#x5730;&#x7531; Nginx &#x7EDF;&#x4E00;&#x7BA1;&#x7406;&#xFF1B;</li>
<li><code>--ban-after=3</code>,<code>--ban-for=60</code>&#xFF0C;&#x5BC6;&#x7801;&#x8F93;&#x9519; 3 &#x6B21;&#x4EE5;&#x540E;&#xFF0C;IP &#x5C01;&#x7981; 60 &#x5206;&#x949F;&#x624D;&#x80FD;&#x518D;&#x6B21;&#x5C1D;&#x8BD5;&#x767B;&#x5F55;&#x3002;Calibre &#x63D0;&#x4F9B;&#x4E86;&#x7B80;&#x5355;&#x7684;&#x9632;&#x7206;&#x7834;&#x4FDD;&#x62A4;&#xFF1B;</li>
<li><code>--auth-mode=basic</code> &#x53EA;&#x4F7F;&#x7528;&#x4E86;&#x57FA;&#x672C;&#x6A21;&#x5F0F;&#x9A8C;&#x8BC1;&#x8EAB;&#x4EFD;&#xFF0C;&#x56E0;&#x4E3A;&#x672C;&#x7AD9;&#x7EDF;&#x4E00;&#x4F7F;&#x7528; https&#xFF0C;&#x7B80;&#x5355;&#x9A8C;&#x8BC1;&#x5373;&#x53EF;&#x3002;</li>
</ul>
<h1 id="0x4nginx">0x4 &#x8BBE;&#x7F6E; Nginx</h1>
<p>&#x65B0;&#x5EFA; <code>books.conf</code>&#xFF1A;</p>
<pre><code>server {
    listen 80;
    listen 443 ssl;
    root /srv/calibre;
    
    # ssl &#x8BC1;&#x4E66;&#x6587;&#x4EF6;&#x548C;&#x5BC6;&#x94A5;
    ssl_certificate /etc/nginx/ssl/pem;
    ssl_certificate_key /etc/nginx/ssl/key;
    
    # &#x4E00;&#x4E9B;&#x52A0;&#x5BC6;&#x8BBE;&#x7F6E;
    ssl_session_timeout 5m;
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1.1 TLSv1.2;
    
    location / {
        if ($ssl_protocol = &quot;&quot;) {
            # http &#x8BF7;&#x6C42;&#x8DF3;&#x8F6C;&#x81F3; https
            return 301 https://books.xcel.me$request_uri;
        }
        
        # &#x5141;&#x8BB8;&#x7684;&#x8BF7;&#x6C42;&#x5927;&#x5C0F;&#x4E0A;&#x9650;&#xFF0C;
        # &#x5982;&#x679C;&#x9700;&#x8981;&#x4E0A;&#x4F20;&#x4F53;&#x79EF;&#x8F83;&#x5927;&#x7684;&#x7535;&#x5B50;&#x4E66;&#xFF0C;&#x8FD9;&#x91CC;&#x9700;&#x8981;&#x9002;&#x5F53;&#x8C03;&#x5927;&#xFF0C;&#x8FD9;&#x91CC;&#x8BBE;&#x7F6E;&#x4E3A; 50M
        client_max_body_size 50M;
        
        # &#x53CD;&#x4EE3;&#x670D;&#x52A1;&#x5230; calibre &#x540E;&#x7AEF;
        proxy_pass http://127.0.0.1:8080;
    }
}
</code></pre>
<p>&#x4FDD;&#x5B58;&#x5E76; reload Nginx&#xFF0C;&#x5B8C;&#x6210;&#x6240;&#x6709;&#x914D;&#x7F6E;&#x3002;</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[一次菜鸟级二进制逆向]]></title><description><![CDATA[某次在 windows 上升级 aria2c 时忘记在用的是改过最大线程数的版本，一不小心覆盖掉了，然而那个版本确实有些久远了，何不拿最新版重新修改一份呢！]]></description><link>https://xcel.me/aria2c-binary-hack/</link><guid isPermaLink="false">5e7f4f90c3ff870e7f60bced</guid><category><![CDATA[Hack]]></category><category><![CDATA[Binary]]></category><category><![CDATA[Aria2]]></category><category><![CDATA[Disassemble]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Sat, 28 Mar 2020 16:40:27 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>&#x67D0;&#x6B21;&#x5728; windows &#x4E0A;&#x5347;&#x7EA7; aria2c &#x65F6;&#x5FD8;&#x8BB0;&#x5728;&#x7528;&#x7684;&#x662F;&#x6539;&#x8FC7;&#x6700;&#x5927;&#x7EBF;&#x7A0B;&#x6570;&#x7684;&#x7248;&#x672C;&#xFF0C;&#x4E00;&#x4E0D;&#x5C0F;&#x5FC3;&#x8986;&#x76D6;&#x6389;&#x4E86;&#xFF0C;&#x7136;&#x800C;&#x90A3;&#x4E2A;&#x7248;&#x672C;&#x786E;&#x5B9E;&#x6709;&#x4E9B;&#x4E45;&#x8FDC;&#x4E86;&#xFF08;<a href="https://everdream.xyz/2017/08/aria2c-spam/">&#x4E0A;&#x6B21;&#x7684;&#x8BE6;&#x60C5;</a>&#xFF09;&#xFF0C;&#x4F55;&#x4E0D;&#x62FF;&#x6700;&#x65B0;&#x7248;&#x91CD;&#x65B0;&#x4FEE;&#x6539;&#x4E00;&#x4EFD;&#x5462;&#xFF01;</p>
<p>&#x867D;&#x7136;&#x54B1;&#x662F;&#x83DC;&#x9E1F;&#xFF0C;&#x5DE5;&#x5177;&#x8981;&#x51C6;&#x5907;&#x597D;&#x54E6;&#x3002;<a href="https://rada.re/n/radare2.html">Radare2</a>&#xFF08;&#x53EF;&#x9009; GUI <a href="https://rada.re/n/cutter.html">Cutter</a>&#xFF09;&#x548C; <a href="https://www.hex-rays.com/products/ida/support/download_freeware/">IDA &#x514D;&#x8D39;&#x7248;</a>&#xFF0C;<s>&#x6740;&#x4EBA;&#x8D8A;&#x8D27;</s>&#x5FC5;&#x5907;&#x795E;&#x5668;&#x3002;</p>
<h1 id="0x0">0x0</h1>
<p>&#x9996;&#x5148;&#x524D;&#x5F80; <a href="https://github.com/aria2/aria2/releases">GitHub</a> &#x4E0B;&#x8F7D;&#x5230;&#x6700;&#x65B0;&#x7248;&#x672C; <code>aria2c.exe</code>&#x3002;&#x7136;&#x540E;&#x6765;&#x89C2;&#x5BDF;&#x4E0B;&#xFF0C;&#x542F;&#x52A8; aria2c &#x65F6;&#x6307;&#x5B9A;&#x7EBF;&#x7A0B;&#x6570;&#x591A;&#x4E8E; 16 &#x4F1A;&#x53D1;&#x751F;&#x4EC0;&#x4E48;&#x5436;&#xFF1A;</p>
<pre><code class="language-shell">C:\Tools\aria2\&gt; .\aria2c.exe --max-connection-per-server=32
Exception: [AbstractOptionHandler.cc:69] errorCode=28 We encountered a problem while processing the option &apos;--max-connection-per-server&apos;.
  -&gt; [OptionHandlerImpl.cc:184] errorCode=1 max-connection-per-server must be between 1 and 16.
Usage:
 -x, --max-connection-per-server=NUM The maximum number of connections to one
                              server for each download.

                              Possible Values: 1-16
                              Default: 1
                              Tags: #basic, #http, #ftp
</code></pre>
<p>&#x5F88;&#x597D;&#xFF0C;&#x6253;&#x5370;&#x51FA;&#x4E86;&#x9519;&#x8BEF;&#x4FE1;&#x606F;&#xFF0C;&#x629B;&#x51FA;&#x5F02;&#x5E38;&#x7684;&#x4F4D;&#x7F6E;&#xFF0C;&#x4EE5;&#x53CA;&#x6B63;&#x786E;&#x7684;&#x7528;&#x6CD5;&#x3002;<code>OptionHandlerImpl.cc</code> &#x629B;&#x51FA;&#x7684;&#x9519;&#x8BEF;&#x4FE1;&#x606F;&#x770B;&#x8D77;&#x6765;&#x5E94;&#x8BE5;&#x662F;&#x6309;&#x6761;&#x4EF6;&#x62FC;&#x51D1;&#x800C;&#x6210;&#xFF0C;&#x8FFD;&#x8E2A;&#x5206;&#x6790;&#x591A;&#x534A;&#x5F88;&#x56F0;&#x96BE;&#xFF0C;&#x6B63;&#x786E;&#x7528;&#x6CD5;&#x7684;&#x8BF4;&#x660E; <code>The maximum number of...</code> &#x8FD9;&#x4E00;&#x5927;&#x6BB5;&#x63CF;&#x8FF0;&#x6587;&#x672C;&#xFF0C;&#x731C;&#x6D4B;&#x5E94;&#x8BE5;&#x662F;&#x4F5C;&#x8005;&#x624B;&#x5199;&#x7684;&#x5B57;&#x7B26;&#x4E32;&#xFF0C;&#x4ECE;&#x8FD9;&#x91CC;&#x5F00;&#x59CB;&#x641C;&#x7D22;&#x5206;&#x6790;&#x5E94;&#x8BE5;&#x4E0D;&#x96BE;&#x3002;</p>
<h1 id="0x1">0x1</h1>
<p>&#x8FDB;&#x5165; radare2&#xFF0C;&#x6211;&#x4EEC;&#x6765;&#x641C;&#x7D22;&#x8FD9;&#x6BB5;&#x6587;&#x672C;&#xFF1A;</p>
<pre><code class="language-shell">C:\Tools\aria2\&gt; r2 .\aria2c.exe
-- &#x2764;&#xFE0F; --
[0x004014e0]&gt; / The maximum number of connections
Searching 33 bytes in [0x8eb200-0x8ec000]
hits: 0
Searching 33 bytes in [0x8eb000-0x8eb200]
hits: 0
Searching 33 bytes in [0x8ea200-0x8eb000]
hits: 0
Searching 33 bytes in [0x8ea000-0x8ea200]
hits: 0
Searching 33 bytes in [0x8e7000-0x8ea000]
hits: 0
Searching 33 bytes in [0x8e2000-0x8e7000]
hits: 0
Searching 33 bytes in [0x8e1200-0x8e2000]
hits: 0
Searching 33 bytes in [0x8a3000-0x8e1200]
hits: 0
Searching 33 bytes in [0x8a2200-0x8a3000]
hits: 0
Searching 33 bytes in [0x87b000-0x8a2200]
hits: 0
Searching 33 bytes in [0x7f5000-0x87b000]
hits: 1
Searching 33 bytes in [0x7f4600-0x7f5000]
hits: 0
Searching 33 bytes in [0x7ee000-0x7f4600]
hits: 0
Searching 33 bytes in [0x7ed600-0x7ee000]
hits: 0
Searching 33 bytes in [0x401000-0x7ed600]
hits: 0
0x00804655 hit0_0 .-per-server=NUM The maximum number of connections to one        .
[0x004014e0]&gt;
</code></pre>
<p>&#x54C7;&#x54E6;&#xFF0C;&#x6700;&#x540E;&#x4E00;&#x884C;&#xFF0C;&#x6211;&#x4EEC;&#x627E;&#x5230;&#x4E86;&#x8FD9;&#x4E00;&#x4E32;&#x6587;&#x672C;&#x7684;&#x5730;&#x5740; <code>0x00804655</code></p>
<h1 id="0x2">0x2</h1>
<p>&#x73B0;&#x5728;&#x8FDB;&#x5165; IDA &#x641C;&#x7D22;&#x5F15;&#x7528;&#xFF0C;&#x6309; G &#x5B9A;&#x4F4D;&#x81F3; <code>0x00804655</code>&#xFF1A;<br>
<img src="https://xcel.me/content/images/2020/03/Screenshot_20200328_230940.png" alt="Screenshot_20200328_230940.png" width="100%"><br>
&#x53EF;&#x4EE5;&#x770B;&#x5230;&#x8FD9;&#x91CC;&#x6709;&#x4E00;&#x5904;&#x6570;&#x636E;&#x4EA4;&#x53C9;&#x5F15;&#x7528; <code>sub_4C2040</code>&#xFF0C;&#x7EE7;&#x7EED;&#x8DDF;&#x4E0A;&#xFF1A;<br>
<img src="https://xcel.me/content/images/2020/03/Screenshot_20200328_232538.png" alt="Screenshot_20200328_232538" width="100%"><br>
&#x6709;&#x4E09;&#x5904;&#x5E38;&#x91CF;&#x5199;&#x5165;&#x5185;&#x5B58; <code>[rsp+188h+var_158]</code>&#xFF0C;<code>[rsp+188h+var_160]</code> &#x4EE5;&#x53CA; <code>[rsp+188h+var_168]</code>&#x3002;&#x6CE8;&#x610F;&#x8FD9;&#x91CC;&#x662F;&#x6587;&#x672C; <code>0x00804655</code> &#x552F;&#x4E00;&#x7684;&#x5F15;&#x7528;&#xFF0C;<code>--max-connection-per-server</code> &#x53C2;&#x6570;&#x7684;&#x4E0A;&#x4E0B;&#x9650;&#x68C0;&#x67E5;&#x5E94;&#x8BE5;&#x5C31;&#x5728;&#x9644;&#x8FD1;&#x3002;&#x5173;&#x6CE8;&#x8FD9;&#x91CC;&#x5199;&#x5165;&#x5185;&#x5B58;&#x7684;&#x4E24;&#x4E2A;&#x5E38;&#x91CF; <code>1</code> &#x548C; <code>10h</code>&#xFF0C;&#x6B63;&#x597D;&#x662F;&#x4E0A;&#x4E0B;&#x9650; 1-16&#xFF0C;&#x731C;&#x6D4B;&#x6709;&#x53EF;&#x80FD;&#x6B63;&#x662F;&#x76EE;&#x6807;&#x503C;&#x3002;</p>
<h1 id="0x3">0x3</h1>
<p>&#x4FEE;&#x6539; <code>0x4c3d70</code> &#x6307;&#x4EE4;&#x7684;&#x503C;&#x4E3A; <code>0FFh</code>&#xFF0C;&#x5BFC;&#x51FA; exe &#x5C1D;&#x8BD5;&#x8FD0;&#x884C;&#xFF0C;&#x4F20;&#x5165;&#x53C2;&#x6570; 32 &#x53EF;&#x4EE5;&#x6B63;&#x5E38;&#x8FD0;&#x884C;&#xFF01;&#x597D;&#x54AF;&#xFF0C;&#x4EFB;&#x52A1;&#x8FBE;&#x6210;&#xFF0C;&#x6700;&#x5927;&#x7EBF;&#x7A0B;&#x6570;&#x4FEE;&#x6539;&#x4E3A; 256&#xFF01;</p>
<hr>
<h1 id="0xf">0xf</h1>
<p>&#x7F51;&#x4E0A;&#x641C;&#x7D22;&#x5076;&#x5C14;&#x770B;&#x5230;&#x6709;&#x795E;&#x4EBA;&#x53EA;&#x7528; vim &#x5C31;&#x80FD; hack &#x4E8C;&#x8FDB;&#x5236;&#xFF0C;&#x4F69;&#x670D;&#x7684;&#x4E0D;&#x8981;&#x4E0D;&#x8981;&#x1F602;&#x5927;&#x4F53;&#x4E0A;&#x662F;&#x8FD9;&#x6837;&#x505A;&#x7684;&#xFF0C;&#x5728; vim &#x91CC; <code>:%!xxd</code> &#x628A;&#x7A0B;&#x5E8F;&#x8F6C;&#x5341;&#x516D;&#x8FDB;&#x5236;&#x6587;&#x672C;&#xFF0C;&#x7136;&#x540E;&#x731C;&#x6D4B;&#x5173;&#x952E;&#x903B;&#x8F91;&#x7684;&#x6307;&#x4EE4;&#xFF0C;&#x7528;&#x4E00;&#x4E32; opcode &#x53BB;&#x641C;&#x7D22;&#x5230;&#x76EE;&#x6807;&#xFF0C;&#x6539;&#x5B8C;&#x540E;&#x518D; <code>:%!xxd -r</code> &#x8F6C;&#x56DE;&#x4E8C;&#x8FDB;&#x5236;&#x4FDD;&#x5B58;&#x3002;</p>
<!--kg-card-end: markdown--><p></p>]]></content:encoded></item><item><title><![CDATA[本地 PAC 代理脚本服务器]]></title><description><![CDATA[Chrome 自某个版本开始，移除了对 pac "file://" URL 的支持，至此只剩下 HTTP 能用了。本地 pac 文件用不了，还得给丢到 http 服务上，单独部署维护多麻烦，那么就写个本地专用的 pac 服务器好啦！]]></description><link>https://xcel.me/local-pacserver/</link><guid isPermaLink="false">5e6d22258980800bdc716b0b</guid><category><![CDATA[programming]]></category><category><![CDATA[Proxy]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Python]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Sat, 14 Mar 2020 14:34:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Chrome &#x81EA;&#x67D0;&#x4E2A;&#x7248;&#x672C;&#x5F00;&#x59CB;&#xFF0C;&#x79FB;&#x9664;&#x4E86;&#x5BF9; pac <code>file://</code> URL &#x7684;&#x652F;&#x6301;&#xFF0C;&#x81F3;&#x6B64;&#x53EA;&#x5269;&#x4E0B; HTTP &#x80FD;&#x7528;&#x4E86;&#x3002;&#x672C;&#x5730; pac &#x6587;&#x4EF6;&#x7528;&#x4E0D;&#x4E86;&#xFF0C;&#x8FD8;&#x5F97;&#x7ED9;&#x4E22;&#x5230; http &#x670D;&#x52A1;&#x4E0A;&#xFF0C;&#x5355;&#x72EC;&#x90E8;&#x7F72;&#x7EF4;&#x62A4;&#x591A;&#x9EBB;&#x70E6;&#xFF0C;&#x90A3;&#x4E48;&#x5C31;&#x5199;&#x4E2A;&#x672C;&#x5730;&#x4E13;&#x7528;&#x7684; pac &#x670D;&#x52A1;&#x5668;&#x597D;&#x5566;&#xFF01;</p>
<p>&#x7CFB;&#x7EDF;&#x4EE3;&#x7406;&#x8BBE;&#x7F6E; PAC &#x811A;&#x672C;&#xFF0C;&#x7528;&#x4EE5;&#x81EA;&#x52A8;&#x5206;&#x6D41;&#x6D4F;&#x89C8;&#x5668;&#x548C;&#x5957;&#x58F3;&#x6D4F;&#x89C8;&#x5668;&#x5E94;&#x7528;&#x7684;&#x8BF7;&#x6C42;&#xFF0C;&#x722C;&#x5899;&#x597D;&#x624B;&#x5927;&#x6982;&#x5BF9;&#x8FD9;&#x4E2A;&#x64CD;&#x4F5C;&#x90FD;&#x5F88;&#x719F;&#x6089;&#x3002;pac &#x672C;&#x8EAB;&#x53EA;&#x662F;&#x4E2A;&#x7B80;&#x5355;&#x7684; js &#x811A;&#x672C;&#xFF0C;&#x7CFB;&#x7EDF;&#x8BBE;&#x7F6E;&#x91CC;&#x5199;&#x597D;&#x5730;&#x5740;&#xFF0C;&#x6D4F;&#x89C8;&#x5668;&#x5C31;&#x4F1A;&#x62FF;&#x666E;&#x901A; http &#x53BB;&#x8BF7;&#x6C42;&#x5230; pac&#x3002;&#x81F3;&#x4E8E;&#x62FF; gfwlist &#x81EA;&#x52A8;&#x751F;&#x6210; pac&#x3001;&#x81EA;&#x52A8;&#x66F4;&#x65B0;&#x7B49;&#x7B49;&#x64CD;&#x4F5C;&#xFF0C;&#x65E9;&#x5728; ss &#x65F6;&#x4EE3;&#x5C31;&#x5DF2;&#x96C6;&#x6210;&#x4E86;&#x3002;</p>
<p>&#x81EA;&#x8F6C;&#x5411;&#x4F7F;&#x7528; v2ray &#x540E;&#xFF0C;&#x6211;&#x53EA;&#x4F7F;&#x7528; v2ray-core &#x4E00;&#x4E2A; exe &#x540E;&#x53F0;&#x8FD0;&#x884C;&#x4EE3;&#x7406;&#xFF0C;&#x5B83;&#x672C;&#x8EAB;&#x6CA1;&#x6709; pac &#x76F8;&#x5173;&#x529F;&#x80FD;&#xFF0C;&#x6240;&#x4EE5;&#x6211;&#x539F;&#x672C;&#x4F1A;&#x5728;&#x672C;&#x5730;&#x653E;&#x4E00;&#x4EFD; pac &#x6587;&#x4EF6;&#xFF0C;&#x7136;&#x540E;&#x7CFB;&#x7EDF;&#x4EE3;&#x7406;&#x8BBE;&#x7F6E;&#x7684;&#x811A;&#x672C;&#x5730;&#x5740;&#x624B;&#x52A8;&#x5199;&#x6210; <code>file:///c/users/auto.pac</code> &#x7684;&#x5F62;&#x5F0F;&#xFF0C;&#x8BA9;&#x6D4F;&#x89C8;&#x5668;&#x76F4;&#x63A5;&#x8BBF;&#x95EE;&#x672C;&#x5730;&#x6587;&#x4EF6;&#x3002;chrome/chromium &#x79FB;&#x9664; <code>file://</code> &#x7684;&#x652F;&#x6301;&#x540E;&#xFF0C;&#x56FE;&#x65B9;&#x4FBF;&#x6211;&#x628A; pac &#x4E22;&#x5230;&#x4E86;&#x672C;&#x7F51;&#x7AD9;&#x670D;&#x52A1;&#x5668;&#x4E0A;&#xFF0C;&#x80FD;&#x7528;&#x4F46;&#x662F;&#x5EF6;&#x8FDF;&#x9AD8;&#xFF0C;&#x5BFC;&#x81F4;&#x6253;&#x5F00;&#x56FD;&#x5185;&#x7F51;&#x7AD9;&#x90FD;&#x4F1A;&#x7ECF;&#x5E38;&#x5361;&#x4F4F;&#xFF0C;&#x4E00;&#x76F4;&#x5728;&#x7B49; pac &#x8BF7;&#x6C42;&#x5B8C;&#x3002;&#x76F4;&#x5230;&#x4ECA;&#x5929; &#x2014;&#x2014;&#x2014;&#x2014; &#x5FCD;&#x4E0D;&#x4E86;&#x9020;&#x4E2A;&#x8F6E;&#x5B50;&#x51FA;&#x6765;&#x5566;&#xFF01;</p>
<p>&#x7A0B;&#x5E8F;&#x5F88;&#x7B80;&#x5355;&#xFF0C;&#x5C31;&#x4E00;&#x4E2A; <code>pacserver.exe</code>&#xFF0C;&#x628A; pac &#x6587;&#x4EF6;&#x547D;&#x540D;&#x6210; <code>autoproxy.pac</code> &#x540E;&#x653E;&#x5230;&#x8DDF; <code>pacserver.exe</code> &#x540C;&#x4E00;&#x4E2A;&#x6587;&#x4EF6;&#x5939;&#x91CC;&#x3002;&#x8FD0;&#x884C;&#x4EE5;&#x540E;&#xFF0C;pac &#x5730;&#x5740;&#x56FA;&#x5B9A;&#x4E3A; <code>http://127.0.0.1:9123/autoproxy.pac</code>&#xFF0C;&#x8BBE;&#x7F6E;&#x6210;&#x7CFB;&#x7EDF;&#x81EA;&#x52A8;&#x4EE3;&#x7406;&#x5730;&#x5740;&#x3002;&#x81F3;&#x4E8E;&#x542F;&#x52A8;&#xFF0C;&#x53EF;&#x4EE5;&#x8DDF; <code>v2ray.exe</code> &#x4E00;&#x6837;&#xFF0C;&#x7528; vbs &#x811A;&#x672C;&#x540E;&#x53F0;&#x8FD0;&#x884C;&#xFF1A;</p>
<pre><code class="language-vbs">createObject(&quot;WScript.Shell&quot;).Run &quot;c:\v2ray\pacserver.exe&quot;,0
</code></pre>
<p><a href="http://dl.xcel.me:9124/pacserver.exe"><code>pacserver.exe</code> &#x70B9;&#x6B64;&#x4E0B;&#x8F7D;</a>&#xFF0C;&#x4F7F;&#x7528; Python3.7 &#x7F16;&#x5199;&#xFF0C;PyInstaller &#x6253;&#x5305;&#xFF0C;&#x6240;&#x6709;&#x6E90;&#x7801;&#x5982;&#x4E0B;&#xFF0C;&#x4E94;&#x5341;&#x884C;&#x4E0D;&#x5230;&#x5C31;&#x641E;&#x5B9A;&#x54AF;&#xFF1A;</p>
<pre><code class="language-python">import http.server
import os

PACFILE_NAME = &apos;autoproxy.pac&apos;
PACFILE = None
PORT = 9123


def prepare_pac():
    global PACFILE
    dir = os.path.dirname(os.path.abspath(__file__))
    os.chdir(dir)
    with open(PACFILE_NAME, &apos;rb&apos;) as f:
        PACFILE = f.read()


class PacRequestHandler(http.server.SimpleHTTPRequestHandler):
    def do_GET(self):
        if self.client_address[0] not in (&apos;::1&apos;, &apos;127.0.0.1&apos;, &apos;localhost&apos;):
            return
        if self.path != &apos;/&apos; + PACFILE_NAME:
            return
        self.send_response(200)
        self.send_head()
        self.wfile.write(PACFILE)


def main():
    prepare_pac()
    http.server.ThreadingHTTPServer((&quot;127.0.0.1&quot;, PORT), PacRequestHandler).serve_forever()


if __name__ == &apos;__main__&apos;:
    main()
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Howto: set proxy for Discord app on linux desktop]]></title><description><![CDATA[Set proxy for Discord app on linux desktop.]]></description><link>https://xcel.me/howto-set-proxy-for-discord-app-on-linux/</link><guid isPermaLink="false">5e3535d5e7f21e2adbf27afd</guid><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Brandon]]></dc:creator><pubDate>Sat, 01 Feb 2020 08:55:31 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Someone one reddit mentioned about how to set proxy for discord app on windows, you can <a href="https://www.reddit.com/r/discordapp/comments/b20a0x/method_of_setting_a_proxy_for_discord/">check this out</a></p>
<p>Generally, there&apos;re some differences between the linux version and the windows version of discord app. The linux version doesn&apos;t need <code>--a</code> parameter to pass some arguments. You just pass <code>--proxy-server</code> to <code>Discord</code> executable.</p>
<p>Copy or modify your <code>discord.desktop</code> file in <code>~/.local/share/applications/</code> or somewhere else similar like this:</p>
<pre><code>[Desktop Entry]
Name=Discord
StartupWMClass=discord
Comment=All-in-one voice and text chat for gamers that&apos;s free, secure, and works on both your desktop and phone.
GenericName=Internet Messenger
Exec=env http_proxy=http://127.0.0.1:8123 https_proxy=http://127.0.0.1:8123 /usr/bin/discord --proxy-server=&quot;http://127.0.0.1:8123&quot;
Icon=discord
Type=Application
Categories=Network;InstantMessaging;
</code></pre>
<p>Pay attention to <code>Exec=</code> line, that part of <code>env http_proxy=... https_proxy=...</code> passes down proxy settings to its updater. <code>--proxy-server</code> passes down proxy settings to the real app. You can use different types of proxy like <code>http://...</code>, <code>socks5://...</code>.</p>
<p>I recommend not to modify the <code>.desktop</code> file under <code>/usr/share/applications</code>, or the settings made above would be lost after next update. Use the one under your home directory in stead. Enjoy!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>