Маниакальный веблог » Webhttps://softwaremaniacs.org/blog/category/web/2019-03-06T00:12:43.041117-08:00ManiacИван Сагалаев о программировании и веб-разработкеhttp://softwaremaniacs.org/media/sm_org/style/photo.jpgTreat HTTP status codes as strings
2018-12-28T20:42:51.711148-08:00https://softwaremaniacs.org/blog/2017/05/13/http-status-codes-strings/I usually see HTTP status codes being checked as integer numbers: if ((status >= 200) && (status < 300)) // or `<= 299` This is rather verbose. But if you think about it, it's the first digit in the number that has a special value, and there are only five ...
<p>I usually see HTTP status codes being checked as integer numbers:</p>
<pre><code>if ((status >= 200) && (status < 300)) // or `<= 299`
</code></pre>
<p>This is rather verbose. But if you think about it, it's the first digit in the number that has a special value, and there are only five of them:</p>
<p><a name=more></a></p>
<ul>
<li><strong>1</strong>xx: server programmer is too smart</li>
<li><strong>2</strong>xx: success</li>
<li><strong>3</strong>xx: your client HTTP library is too dumb</li>
<li><strong>4</strong>xx: you screwed up</li>
<li><strong>5</strong>xx: server screwed up</li>
</ul>
<p>When treated as strings, checking for the error class looks a bit better:</p>
<pre><code>if (status[0] == '2')
</code></pre>
<p>Unfortunately, the ensuing party is pre-pooped by most client HTTP libraries helpfully casting the status to <code>int</code>.</p>
<p class=strong><strong>Dear HTTP client libraries! Consider adding <code>response.error_class</code> to your responses.</strong>Versioning REST: another angle
2015-07-27T16:31:06.427000-07:00https://softwaremaniacs.org/blog/2015/07/27/versioning-rest-another-angle/I've got an interesting comment on "Versioning REST APIs" that boils down to these points: Sometimes you can't really afford breaking clients (ever, or long enough to make no matter). A global version allows to freeze an old code base and have new implementation to be completely independent. This is ...
<p>I've got an interesting comment on "<a href="http://softwaremaniacs.org/blog/2015/07/21/versioning-rest-apis/en/">Versioning REST APIs</a>" that boils down to these points:</p>
<ol>
<li>Sometimes you can't really afford breaking clients (ever, or long enough to make no matter).</li>
<li>A global version allows to freeze an old code base and have new implementation to be completely independent.</li>
</ol>
<p>This is a different situation from the one I had in mind <a href="http://softwaremaniacs.org/blog/2015/07/24/versioning-followup/en/">where breaking changes do eventually happen</a> at some point.</p>
<p><a name=more></a></p>
<h2>Technically…</h2>
<p>Technically, no matter how you look at the issue, per-resource versioning gives you <em>more</em> flexibility than a global version number: changing version of every resource representation has the same effect as changing the global number.</p>
<p>From a practical standpoint it is as simple as having code somewhere checking the Accept header in all requests:</p>
<pre><code>Accept: application/mytype+json; version=2
</code></pre>
<p>… and doing different things or even dispatching to completely different services depending on the version.</p>
<p>Even if you want to invent a completely different URL scheme for your API, it's still <em>technically</em> either changing representations of existing resources, or adding new ones (we can't remove anything under condition 1.)</p>
<h2>However…</h2>
<p>I could see a tangential benefit in having a global version only in cases such as these:</p>
<ul>
<li>You want to abandon the old code base and don't even want to maintain header-base routing code in your new one.</li>
<li>You're changing semantics of resources, effectively making it a completely different product.</li>
</ul>
<p>But replacing the whole Universe with the new one does (or should) happen much less often than resource-local breaking changes that could be handled by per-resource versioning without affecting the whole API. If global version is your only mechanism than you have to change worlds all the time, even when you don't have to.Versioning: follow-up
2017-11-18T01:52:15.606978-08:00https://softwaremaniacs.org/blog/2015/07/24/versioning-followup/After reading a few comments on reddit and by email about my post on versioning of REST APIs I see that I wasn't clear on terminology and have left out some context. That's okay! Overthinking details is the main killer of all my interesting thoughts :-) I'd rather post more ...
<p>After reading a few <a href="https://www.reddit.com/r/programming/comments/3e92dw/versioning_of_rest_apis_dont_version_apis_version/">comments on reddit</a> and by email about my post on <a href="http://softwaremaniacs.org/blog/2015/07/21/versioning-rest-apis/en/">versioning of REST APIs</a> I see that I wasn't clear on terminology and have left out some context. That's okay! Overthinking details is the main killer of all my interesting thoughts :-) I'd rather post more and repeat myself on occasion.</p>
<p><a name=more></a></p>
<h2>What's "backwards incompatible", exactly?</h2>
<p>The main point of contention was that there's no problem, really: if an API changes incompatibly, existing clients could just use the old version of an API. Hence, a version bump in the URL is seen as a <em>solution</em>, letting many versions to co-exist in the public space. There was even a suggestion to keep <em>all versions indefinitely</em>.</p>
<p>Well, consider these situations, off the top of my head:</p>
<ul>
<li>
<p>You want to <em>remove</em> a feature from an API for business reasons. Keeping an old version simply defeats the purpose.</p>
</li>
<li>
<p>Someone found a security hole in an old version. Even if there's no bug anymore in the current version, you will have to dig through all the active old ones and patch them. The more of them, and the older they are, the more costly it becomes. Sometimes a fix would make a version backwards incompatible which, again, defeats the purpose of keeping it.</p>
</li>
<li>
<p>With growing load, your API needs a performance overhaul by, say, replacing granular update operations with a bulk one. Even if you could technically keep the old version active, you don't want that, because the point is to get rid of the performance bottleneck.</p>
</li>
</ul>
<p>I'm going to go ahead and even postulate this:</p>
<p class=strong><strong>if you are able to keep an old version of the API working alongside a new one, you shouldn't make the change backwards incompatible at all.</strong></p>
<h2>Why invalidate clients</h2>
<p>So, when old functionality eventually dies, <em>even after you supported it for some time</em>, the clients that use it are invalidated. It is not a question of "if".</p>
<p>The question is, then, "how":</p>
<ul>
<li>You either explicitly change the version number in the root of your URLs, or</li>
<li>you change versions and/or representations of only those resources that has suffered the change.</li>
</ul>
<p>The crux of my argument in the previous post was that the latter is better because it avoids invalidating clients that don't use the broken resource.</p>
<p>A simple to imagine example would be a read/write API, like Twitter's. If you change the writing, your clients that only read don't even need to know about it. Which may be a majority of them. Or change in the authentication protocol that doesn't affect anonymous clients.</p>
<h2>Miscellaneous</h2>
<p>This bit caused some confusion, too:</p>
<blockquote>
<p>It constrains development on the server as you have to synchronize releases of independent backwards incompatible features to fit them into one version change.</p>
</blockquote>
<p>Here, I shouldn't have used the word "have". What I meant is that since you want to minimize the amount of breaking changes, you <em>might</em> want to shove many little breaking changes that's been cooking up in your code base into one public update. This requires synchronizing.</p>
<p>But I admit that, while completely real, it's rather a weak point.Versioning of REST APIs
2015-07-25T00:57:00.434000-07:00https://softwaremaniacs.org/blog/2015/07/21/versioning-rest-apis/Don't version APIs, version resources. I.e., this is wrong: https://example.com/api/v1/resource Global version number has a few problems: A backwards incompatible change to any one resource invalidates all clients, even those who don't use this particular resource. This is unnecessary maintenance burden on client developers. It constrains development on the server ...
<p>Don't version APIs, version resources. I.e., this is wrong:</p>
<pre><output><a href="https://example.com/api/">https://example.com/api/</a><b>v1</b>/resource</output></pre>
<p><a name=more></a></p>
<p>Global version number has a few problems:</p>
<ul>
<li>
<p>A backwards incompatible change to <em>any</em> one resource invalidates all clients, even those who don't use this particular resource. This is unnecessary maintenance burden on client developers.</p>
</li>
<li>
<p>It constrains development on the server as you have to synchronize releases of independent backwards incompatible features to fit them into one version change.</p>
</li>
<li>
<p>You have to maintain several versions of your <em>whole API code base</em> simultaneously.</p>
</li>
</ul>
<p>May be there's more, but the first problem is quite enough, if you ask me. And I can't think of a single disadvantage of versioning resources independently.</p>
<h2>Versioning resources</h2>
<p>Technically, you can do this:</p>
<pre><code>Content-type: application/myformat+json; <b>version=2.0</b></code></pre>
<p>… or this:</p>
<pre><code>{
<b>"version": "2.0"</b>,
"payload": { ... }
}
</code></pre>
<p>… or even this:</p>
<pre><output><a href="https://example.com/api/resource">https://example.com/api/resource</a>?<b>version=2.0</b></output></pre>
<p>It doesn't matter. Since we don't live in the pure-REST utopia predominantly using well-defined MIME types, your representation formats are probably custom anyway, so your versioning is going to be custom as well. As long as it's documented, it's fine.</p>
<h2>Implicit versioning</h2>
<p>Instead of giving your format an explicit version number, you can change data in a way that the old clients could not use it. For example, if you've subtly changed the format of a date field, change the name of the field too:</p>
<p>Version 1:</p>
<pre><code>{"time": "2012-01-01T00:00"}
</code></pre>
<p>Version 2:</p>
<pre><code>{"utctime": "2012-01-01T08:00"}
</code></pre>
<p>You could even support both fields for some time with a deprecation warning in the docs.</p>
<p>Even though I like this approach more than numbering, I'm not going to defend it to the death. I think it boils down to the kind of developers you want to cater to (including your own). Some people believe that everything should be declared in advance, preferably with a formal schema. But schemas are harder to maintain. Other people rely on catching exceptions at run-time and introspection. But run-time sometimes means "production" and bad things might happen more often than you'd like.Problem with JSON encoding
2015-03-22T22:33:15.595000-07:00https://softwaremaniacs.org/blog/2015/03/22/json-encoding-problem/JSON spec says that a UTF shall be used to encode it as a byte stream (with a strong preference to UTF-8). However it also allows characters to be encoded in the form of \uXXXX. This poses a problem on the parser implementation side: You have to decode a byte ...
<p>JSON spec says that <a href="https://tools.ietf.org/html/rfc7159#section-8.1">a UTF shall be used</a> to encode it as a byte stream (with a strong preference to UTF-8). However it also allows characters to be encoded <a href="https://tools.ietf.org/html/rfc7159#section-7">in the form of <code>\uXXXX</code></a>. This poses a problem on the parser implementation side:</p>
<p><a name=more></a></p>
<ul>
<li>
<p>You have to decode a byte stream twice: first convert a UTF encoding to Unicode code points and then replace any <code>\uXXXX</code> sequences with a single code point.</p>
<p>The <code>\u</code> encoding is redundant as you can encode all of Unicode in a UTF just fine. The only technical reason that I can see in the RFC for this is that a UTF encoding is a SHALL, not a MUST.</p>
</li>
<li>
<p>Your language runtime probably doesn't make the second step easy.</p>
<p>Modern languages that I'm familiar with use distinct data types for UTF-encoded sequences of bytes and Unicode characters. So even if your runtime has a built-in codec for <code>\uXXX</code> escapes it probably expects a sequence of byte on input to produce a sequence of Unicode characters on output. But treating your input stream first as UTF-encoded produces those <code>\uXXXX</code> as characters already, not bytes. So you can't use you library codec and have to decode those manually, which is brittle and silly.</p>
</li>
</ul>
<p>I'm writing this as a comment to Tim Bray's "<a href="https://www.tbray.org/ongoing/When/201x/2015/03/23/i-json">I-JSON</a>" which is an emerging description of a stricter JSON aimed to avoid interoperability problems with JSON (<a href="https://tools.ietf.org/html/rfc7493">RFC 7493</a>). So here's my comment/proposal in a nutshell:</p>
<ol>
<li>Mandate a UTF encoding with a "MUST", not a "SHALL".</li>
<li>Drop the <code>\uXXXX</code> escapes.</li>
</ol>
<p>Makes sense?Anti popup policy
2014-09-07T15:32:46.668000-07:00https://softwaremaniacs.org/blog/2014/09/07/anti-popup-policy/I just adopted a new policy. Whenever I come upon an article that I want to read and possibly share, if the site obstructs my reading with a popup telling me to take part in their survey (or some such, I don't really pay attention to them) I respectfully close ...
<p>I just adopted a new policy. Whenever I come upon an article that I want to read and possibly share, if the site obstructs my reading with a popup telling me to take part in their survey (or some such, I don't really pay attention to them) I <em>respectfully</em> close the whole tab with "No, thank you" and head over to Google to find the same topic on another site. And I share those instead.</p>
<p>What if there aren't any alternatives? Well, in most cases it turns out to be not that awfully important anyway. So I move on.</p>
<p>I urge everyone to adopt this policy. It's the only way they learn.OAuth is not a protocol
2013-01-09T16:07:52.971000-08:00https://softwaremaniacs.org/blog/2012/07/30/oauth-is-not-a-protocol/Though this post is obviously triggered by the recent damnation of OAuth 2.0 by the (former) spec editor Eran Hammer, it's not directly related to it. These are my thoughts about the technical role of OAuth that I wanted to blog about a year ago but couldn't force myself to ...
<p>Though this post is obviously triggered by the recent <a href="http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/">damnation of OAuth 2.0</a> by the (former) spec editor Eran Hammer, it's not directly related to it. These are my thoughts about the technical role of OAuth that I wanted to blog about a year ago but couldn't force myself to do it. But as of now, it seems surprisingly relevant!</p>
<p>My experience with OAuth comes from being a project manager of the <a href="http://api.yandex.com/oauth/">OAuth server at Yandex</a> where I took part in the design, closely watched the implementation (up to about draft 16 of the spec) and helped writing developer docs.</p>
<p><a name=more></a></p>
<h2>Too much freedom</h2>
<p>What always struck me as odd was the amount of details in the OAuth spec that are implementation dependent. Here are some from the top of my head that I still remember:</p>
<ul>
<li>Endpoint URLs are neither fixed nor is there any discovery mechanism for them.</li>
<li>Application registration is mentioned but doesn't have any properties upon which implementors could rely.</li>
<li>At one point the <code>client_secret</code> wasn't required leaving client authorization basically undefined too.</li>
<li>The authorization scope — both the set of permissions and their semantics — is completely service specific.</li>
<li>There are several ways to define a scope for a client: either during registration of an application or during an authorization request.</li>
<li>There are no guarantees about the rights implied by the access token: nothing prevents a service from limiting the authorization scope of a token based on meta data or, say, user's decision.</li>
</ul>
<p>Leaving those vast undefined grounds actually does make sense because real-world APIs differ immensely and creating a single detailed consistent authorization framework covering all their use cases is at the least impractical, if not impossible.</p>
<h2>Libraries</h2>
<p>All this means that it's impossible to devise such a thing as a "generic OAuth library" that could perform the complete authorization flow against an arbitrary API. As I understand it, current OAuth libraries provide service for encoding language-native data into GET requests and parsing JSON responses. The rest — designing all of the UI and setting up callback URLs — is still application specific. Actually those applications that talk to one particular API don't even need all that library code supporting different real-world flavors of OAuth and would do fine with plain HTTP and JSON libraries. The only thing that a dedicated OAuth library really buys them is the luxury of reading developer-oriented docs instead of a formal spec (which is no fun at all).</p>
<p>By the way, the very fact that so many different OAuths are still floating around in the wild is an indication that developers don't see the point of converging their code to match a single spec since most of said code will remain service-specific anyway.</p>
<h2>Not a protocol?</h2>
<p>The point of any Internet protocol is enabling interoperability between arbitrary parties without additional peer-to-peer agreements. Your HTTP client can (mostly) talk to any HTTP server on the Web without so much as even learning its name. Since it doesn't seem to be the case with OAuth, wouldn't it be better to treat it as a set of best practices instead of a protocol? An "architectural style" if you will…</p>
<p>And may be there should be really two different things:</p>
<ul>
<li>"WebAuth": a set of guidelines with all the different authorization flows and security considerations defined by current OAuth spec,</li>
<li>"OLogin": a protocol with concrete endpoints, common permissions and client identification intended for a use-case of "Login with Something" that seems to be one thing that many different APIs share.</li>
</ul>
<p>Does it make sense to anyone?Rainbow.js — a new kid on the highlighters' block
2012-09-07T01:35:24.504000-07:00https://softwaremaniacs.org/blog/2012/03/26/rainbow-js/There was a small spike in my referrers stats that led me to a new JavaScript highlighting library — rainbow.js. And since I love bashing other highlighters I couldn't resist this time too :-). Oh, but be sure that all of this is intended of course as a constructive criticism ...
<p>There was a small spike in my referrers stats that led me to a new JavaScript highlighting library — <a href="http://craig.is/making/rainbows">rainbow.js</a>. And since I love <a href="http://softwaremaniacs.org/blog/2011/05/22/highlighters-comparison/en/">bashing other highlighters</a> I couldn't resist this time too :-). </p>
<p>Oh, but be sure that all of this is intended of course as a constructive criticism only!</p>
<p><a name=more></a></p>
<h2>Size claim</h2>
<p>It says upfront that it's 1.2K in size. It isn't. If you include 5 languages it currently supports — it's 8.1K. Which is still impressive given that highlight.js is 11.8K with the same languages.</p>
<h2>Features</h2>
<p>It doesn't have any beside highlighting itself. No user markup, no line numbers, no language detection, etc. But as far as I understand, it's a design goal. And I can only wish the author <em>a lot</em> of patience in <del>telling people to shut up</del> <ins>carefully evaluating feature requests</ins>!</p>
<h2>Correctness</h2>
<p>This is where things get ugly, unfortunately. I loaded up my test suite and on the spot found these:</p>
<ul>
<li>prefixed strings in Python (<code>r""</code>, <code>u""</code>) aren't detected</li>
<li>tripple-quote strings in Python are detected wrong (first two quotes are treated as strings)</li>
<li>backslash escapes in strings aren't detected which can break the whole further highlighting in cases like <code>"a \" b"</code></li>
<li>names of old-style Python classes are not recognized (because of the lack of parens after the names)</li>
<li>doctype declaration in HTML is treated as a tag</li>
<li>tag attributes in HTML aren't detected reliably, like "checked" here: <code><input checked type="checkbox"></code></li>
<li>in the CSS snippet <code>{margin: 1cm 2cm 1.3cm 4cm;}</code> "1." and "4cm" are not recognized as values</li>
<li>in <code>div {width: 100%}</code> "100%" is not recognized as a value</li>
<li>in the selector <code>p[lang=en]</code> all "p", "lang" and "en" are detected as tags</li>
<li>in JavaScript literal regexps are not distinguished from devision operators which leads to all sorts of breakage</li>
</ul>
<p>I'm sure there are many other bugs because…</p>
<h2>Speculation</h2>
<p>… rainbow.js employs <a href="https://github.com/ccampbell/rainbow/blob/master/js/language/generic.js">generically defined lexing</a> for all supported languages. Which is good for keeping the library fit and slender but won't work for all the sheer insanity of syntaxes that humanity cared to invent over the latest half a century. </p>
<p>There are backslash escapes and double-quote escapes for strings. PHP, Ruby, Shell all allow embedded code within certain types of strings to a certain extent. JavaScript has literal regexps that clash with division. Pascal has different syntax for hex numbers. Lines starting with # are comments in many languages but in C they're preprocessor directives. And don't even get me started on <a href="https://github.com/isagalaev/highlight.js/blob/master/src/languages/perl.js">Perl</a>…</p>
<p>All in all I think that current design of rainbow.js won't allow it to grow past a family of not-too-conflicting language syntaxes. Which puts it in the same position as <a href="http://code.google.com/p/google-code-prettify/">Google Code Prettify</a>. Which for me means that we don't have to worry about this competition yet. But Google should :-). </p>
<p>Anyway I wish the best of luck to Craig Campbell in his endeavor!</p>
<h2>Envy</h2>
<p>I totally envy their site design!!!WebM год спустя
2012-02-09T16:31:33.030000-08:00https://softwaremaniacs.org/blog/2012/02/09/webm-one-year-later/Помните, около года назад я тщетно пытался понять, почему люди стали возмущаться в адрес Гугла, после того, как тот объявил о прекращении поддержки кодека H.264 в Хроме? Вот недавно я наткнулся на пост веб-стандартиста Хенри Сивонена со сравнительным исследованием доли кодеков сейчас на вебе, где он посчитал, что WebM-браузеров в ...
<p>Помните, около года назад я <a href="http://softwaremaniacs.org/blog/2011/01/11/no-h264-in-chrome/">тщетно пытался понять, почему люди стали возмущаться в адрес Гугла</a>, после того, как тот объявил о прекращении поддержки кодека H.264 в Хроме? Вот недавно я наткнулся на пост веб-стандартиста Хенри Сивонена со сравнительным исследованием доли кодеков сейчас на вебе, где он посчитал, что <a href="http://hsivonen.iki.fi/webm-share/">WebM-браузеров в мире стало больше, чем H.264</a>. Порадовавшись за WebM, я решил посмотреть, как сбываются мои собственные предсказания годовалой давности.</p>
<p><a name=more></a></p>
<p>Вот, что я тогда думал:</p>
<blockquote>
<ol>
<li>YouTube начнёт весь новый контент кодировать в WebM вместе с H.264</li>
<li>Новые поколения девайсов будут использовать железо с поддержкой WebM из коробки. В iPhone поддержка будет выключена софтверно.</li>
<li>Как только WebM-контента станет достаточно много, Google объявит, что YouTube через какое-то время перестанет кодировать в H.264</li>
<li>Apple включит поддержку WebM в девайсах и в Сафари.</li>
<li>H.264 отомрёт</li>
</ol>
</blockquote>
<p>Конечно, прогноз был долгосрочный, и уже сейчас видно, что ландшафт изменился: мы теперь знаем, например, что Стив Джобс не с нами, а Adobe официально отказалась от развития Flash для мобильных платформ. Но мне приятно, что <em>полтора пункта</em> сбылись :-).</p>
<p>Ответственно заявляю, что с YouTube сейчас уже практически можно жить с Firefox'ом без Flash. Большинство роликов, которые я смотрю — это нативное браузерное WebM видео. Flash только иногда попадается. Думаю, тенденция продолжится и дальше.</p>
<p>Про аппаратную поддержку я смог найти две ссылки:</p>
<ul>
<li><a href="http://finance.yahoo.com/news/rockchip-release-android-4-0-145300601.html">Rockchip месяц назад заявил о выпуске WebM-чипа, ориентированного на Android-устройства</a></li>
<li><a href="http://www.broadcom.com/press/release.php?id=s471536">Broadcom выпускает чип с WebM уже скоро как два года</a></li>
</ul>
<p>Это, конечно, далеко от <em>массовой</em> поддержки, поэтому этот пункт своего прогноза я засчитал за половину.</p>
<p>А знаете, что забавно? Поддержка H.264 в Хроме так до сих пор никуда и не делась :-).Continuation-based фреймворки
2011-10-24T00:02:41.952000-07:00https://softwaremaniacs.org/blog/2011/10/23/continuation-based-frameworks/Макс Лапшин написал о том, куда нужно двигаться Рельсам на примере Nitrogen, и резюмировал: Важно понять, что код тут выходит за рамки классического «запрос-ответ» и позволяет в процессе обработки долгоживущего процесса что-то рисовать в браузере. […] даже на таком маленьком примере кода ясно, что классический подход а-ля php: reply on ...
<p>Макс Лапшин написал о том, <a href="http://levgem.livejournal.com/369150.html">куда нужно двигаться Рельсам на примере Nitrogen</a>, и резюмировал:</p>
<blockquote>
<p>Важно понять, что код тут выходит за рамки классического «запрос-ответ» и позволяет в процессе обработки долгоживущего процесса что-то рисовать в браузере.</p>
<p>[…]</p>
<p>даже на таком маленьком примере кода ясно, что классический подход а-ля php: reply on request уже не актуален. Нотификация с сервера на клиент может пройти когда угодно и теперь надо это учитывать.</p>
</blockquote>
<p>Меня зацепило словосочетание "уже не актуален". На самом деле, идея построения серверного кода в виде как бы непрерывной функции, когда общение с браузером абстрагируется куда-нибудь внутрь, не нова. Это называется continuation-based фреймворки, и они есть не только на Erlang, но <a href="http://en.wikipedia.org/wiki/Continuation#Continuations_in_Web_development">и на Ruby, и на Python, и на Smalltalk</a>. А у лисперов (и особенно схемеров) это вообще любимое упражнение, как я заметил.</p>
<p><a name=more></a></p>
<p>Однако несмотря на то, что попыток было много, большая часть веб-программирования продолжает делаться в запрос-ответном стиле. И long-poll тоже укладывается в этот подход.</p>
<p>Я не знаю, будет ли так всегда. Возможно, все попытки до сих пор были просто неудачно реализованы, или не хватало каких-то ярких примеров, или в браузерных технологиях нет какой-то мелочи, мешающей этому подходу выстрелить. Но я знаю, что запрос-ответная схема — это не какая-то устаревшая концепция, это, на самом деле, тот самый REST — одна из фич веба. Есть мнение, что именно из-за неё он победил всё остальное. И если continuation-based фреймворки хотят быть следующей ступенью эволюции, им надо будет по крайней мере предложить что-то взамен того, что предлагает REST. Начать хотя бы с универсальной адресуемости состояния сервиса.Разница между OpenID и OAuth
2012-07-30T17:49:51.538000-07:00https://softwaremaniacs.org/blog/2011/07/14/openid-oauth-difference/Как ни странно, путаница в понятиях "OpenID" и "OAuth" встречается довольно часто. Люди используют их как взаимозаменяемые термины или считают OAuth своего рода новой улучшенной версией OpenID. Но на самом деле это совершенно разные вещи. Чтобы было куда ссылаться в таких случаях, пишу пост. Этот пост не о том, что ...
<p>Как ни странно, путаница в понятиях "OpenID" и "OAuth" встречается довольно часто. Люди используют их как взаимозаменяемые термины или считают OAuth своего рода новой улучшенной версией OpenID. Но на самом деле это совершенно разные вещи. Чтобы было куда ссылаться в таких случаях, пишу пост.</p>
<p class=note><small>Этот пост <em>не о том</em>, что из этих двух слов "лучше", что "зло" или что "умерло". Это просто констатация фактов.</small></p>
<p><a name=more></a></p>
<h2>Суть</h2>
<p class="picture right"><img src="/media/blog/openid-logo.png"></p>
<p><strong>OpenID</strong> позволяет сайту удостовериться, что его пользователь владеет неким персональным URL (своим сайтом, блогом, профилем). Этого факта достаточно для того, чтобы использовать уникальный URL для узнавания того же самого пользователя в следующий раз. И всё. Все остальные вещи — заведение аккаунта, получение email'а и других данных, разрешение какой-то активности на сайте — остаётся на усмотрение сайта. Другими словами, OpenID — это чистая <strong>аутентификация</strong>: вы знаете, кто к вам пришёл, но вольны делать с этим знанием всё, что угодно.</p>
<p class="picture right"><img src="/media/blog/oauth-logo.png"></p>
<p><strong>OAuth</strong> позволяет программе (на вебе или локальной) получить от пользователя права на использование какого-то конкретного API. Права обозначаются токеном, свойства которого никак не определены: он может быть одинаковым для разных пользователей, может быть разным для одного в разное время. Всё, что гарантируется — это что программа в обмен на токен сможет выполнять какие-то действия на каком-то сервисе. Другими словами, OAuth — это чистая <strong>авторизация</strong>: вы обладаете конкретными правами, но не можете в общем случае по ним определить, кому они принадлежат.</p>
<p>Аналогия. OpenID — это ваш паспорт: он говорит, кто вы, но что он даёт, зависит от места, куда вы с ним пришли. OAuth — ключи от вашей машины: с ними можно ездить на вашей машине, даже не зная, как вас зовут.</p>
<h2>Вход на сайты</h2>
<p class="picture right"><img src="/media/blog/twitter-facebook.png"></p>
<p>Исходя из написанного выше, естественной технологией для реализации входа на сайты с существующим сторонним аккаунтом является OpenID. Больше того, из-за неопределённости понятия токена "входа через OAuth" вообще не может существовать в природе. Тем не менее, вездесущие входы через Твиттер и Фейсбук используют как раз OAuth. Где неувязка?</p>
<p>И Твиттер, и Фейсбук используют OAuth, как деталь реализации, но в остальном у них нет ничего общего. Роли протоколов выглядят в этом случае так:</p>
<ul>
<li>OAuth предоставляет сайту <strong>авторизацию</strong> на использование API Твиттера/Фейсбука</li>
<li>а вот уже через API можно провести <strong>аутентификацию</strong> ("вход") пользователя, так как оба они предоставляют некую идентифицирующую информацию</li>
</ul>
<p>Аналогия. "Вход через OAuth" — это ключи от машины, в которой по случаю оказались ещё и именные документы владельца.</p>
<p>Важно понимать, что поскольку аутентификацию предоставляет проприетарный API, это не "вход через OAuth", а "вход через Твиттер/Фейсбук". На практике это значит, что нельзя написать некую обобщённую OAuth-библиотеку для входа через любой произвольный сервис типа Твиттера и Фейсбука. </p>
<p>Хотя…</p>
<h2>OpenID Connect</h2>
<p>Идея обобщённого входа через OAuth может работать, если вместо проприетарного API аутентификации стандартизовать какой-нибудь открытый. И такая попытка была — <a href="http://openidconnect.com/">OpenID Connect</a>. Там главная идея OpenID — открытая аутентификация — отделяется от протокола обмена ключами, в качестве которого берётся как раз OAuth. И ещё решаются сопутствующие вопросы типа discovery-механизма, которого в OAuth нет.</p>
<p>Впрочем, на практике OpenID Connect нигде не используется. Почему — совсем отдельная тема.О распространённости H.264
2011-01-12T11:26:48.012000-08:00https://softwaremaniacs.org/blog/2011/01/12/h264-standard-de-facto/Небольшое разъяснение к предыдущему посту. Я и не надеялся, что понятно изложу свою точку зрения сразу :-). Некоторые комментаторы с изрядной долей язвительности рассказыли мне, что я, видимо, не в курсе, что H.264 сейчас является стандартом де-факто. Причём не только в вебе, но и на телевидении. Поверьте, я знаю. И ...
<p>Небольшое разъяснение к <a href="http://softwaremaniacs.org/blog/2011/01/11/no-h264-in-chrome/">предыдущему посту</a>. Я и не надеялся, что понятно изложу свою точку зрения <em>сразу</em> :-).</p>
<p>Некоторые комментаторы с изрядной долей язвительности рассказыли мне, что я, видимо, не в курсе, что H.264 сейчас является стандартом де-факто. Причём не только в вебе, но и на телевидении. </p>
<p>Поверьте, я знаю. И Google, конечно, тоже знает. Однако я не вижу, как это опровергает написанное. Да, он самый распространённый, но <em>это как раз и плохо</em>. Потому что патентованный. Потому что Mozilla и Opera не согласны. Потому что закрытый.</p>
<p>Всё, о чём я пишу — это то, что Google, похоже, собрался медленно менять статус-кво.Об изъятии H.264 из Google Chrome
2011-01-11T23:40:35.017000-08:00https://softwaremaniacs.org/blog/2011/01/11/no-h264-in-chrome/Разработчики Google Chrome объявили, что через какое-то время их браузер престанет поддерживать кодек H.264. То, что многие на новость отреагировали — не удивительно. Удивительно, что мои фолловеры в Твиттере разделились на две чёткие группы: англоязычные технари поздравляют друг-друга и Гугл, русскоязычные технари в отчаянии и Гугл клянут. Я всей душой ...
<p>Разработчики Google Chrome объявили, что через какое-то время <a href="http://blog.chromium.org/2011/01/html-video-codec-support-in-chrome.html">их браузер престанет поддерживать кодек H.264</a>. То, что многие на новость отреагировали — не удивительно. Удивительно, что мои фолловеры в Твиттере разделились на две чёткие группы: англоязычные технари поздравляют друг-друга и Гугл, русскоязычные технари в отчаянии и Гугл клянут. </p>
<p>Я всей душой с англоязычными коллегами, и не вижу в этом ничего плохого. Постараюсь изложить.</p>
<p><a name=more></a></p>
<h2>Почему не надо бояться</h2>
<p>Из того, что я понял (а я уверен, что далеко не всё и не всех: Твиттер — не самое удобное средство для разъяснения мыслей), люди боятся того, что теперь нельзя будет смотреть HTML5 видео, что производители девайсов не смогут переделать девайсы под WebM, и что производителям контента придётся массово перекодировать всё в новый кодек.</p>
<p>Давайте по порядку: поддержка H.264 изымается из <em>Google Chrome</em>. И это (пока) всё. Следовательно, "всё сломается" только у тех, кто смотрит видео, доступное только через тег <code><video></code>, закодированное только в H.264, и пользуется при этом только Chrome'ом. Таких не много.</p>
<p>Уверяю вас, YouTube (Blip, Vimeo и т.д.) продолжит работать в Chrome через Flash. H.264 контент YouTube, доступный в девайсах через нативные плееры, тоже никуда не денется. Поэтому для подавляющего большинства <em>потребителей</em> ничего не изменится. Поводов для паники нет.</p>
<h2>Что хорошего</h2>
<p>До настоящего момента тег <code><video></code> был, вообще-то, обречён. Если вы выдаёте контент в H.264, его не видит больша́я часть людей, использующих Firefox. Если вы выдаёте контент в Ogg Theora, его не видите вы сами в своём Safari, а также его не видят люди на мобильных девайсах. Производить контент в двух форматах накладно. Поэтому феномен "HTML5 видео" до сих пор находится в полудохлом состоянии.</p>
<p>Отказ от поддержки H.264 в браузере в тех формулировках, в которых написан, не меняет ничего прямо сейчас. Но это заявление может предполагать долгосрочную стратегию Google в продвижении кодека WebM.</p>
<p>Вот моя (в меру наивная) догадка о том, куда это всё должно двигаться:</p>
<ol>
<li>YouTube начнёт весь <em>новый</em> контент кодировать в WebM <em>вместе</em> с H.264</li>
<li>Новые поколения девайсов будут использовать <a href="http://www.webmproject.org/about/supporters/#hardware">железо с поддержкой WebM</a> из коробки. В iPhone поддержка будет выключена софтверно.</li>
<li>Как только WebM-контента станет достаточно много, Google объявит, что YouTube через какое-то время перестанет кодировать в H.264</li>
<li>Apple включит поддержку WebM в девайсах и в Сафари.</li>
<li>H.264 отомрёт</li>
</ol>
<p>В этом сценарии в проигрыше остаются только <a href="http://indiworks.wordpress.com/2010/05/18/h-264-list-of-shame-all-the-patent-holders/">патент-холдеры H.264</a> (среди которых, кстати, есть Apple). И хотя времени для перерасчёта бюджетов у них полно, боюсь, просто так они от такой кормушки не откажутся.</p>
<h2>Спекуляция</h2>
<p>Это полусерьёзная часть, не стоит меня чехвостить за неё в комментариях, пожалуйста :-). </p>
<p>Кажется, я догадываюсь, почему часть моего Твиттера так расстроилась. Большинство из вас, ребята, пользователи Маков. И вам из своей "echo chamber" кажется, что H.264 уже давно победил, а Google вдруг решил убить этот единственно возможный для всех кодек. Примерно ту же реакцию я наблюдал в начале 2000-х у веб-мастеров, привыкших верстать под IE. Они ненавидели веб-стандарты, которые разрушали их представление о том, что IE окончательно стал единственным браузером в вебе, и теперь всё "просто".</p>
<p>Посмотрите, где мы сейчас :-).Рестайлинг
2010-09-04T12:43:47.115000-07:00https://softwaremaniacs.org/blog/2010/09/04/restyling-2010/Сделал на сайте небольшой рестайлинг, который коснулся в основном блога и форума. Вспомнил заржавевшие CSS'ные скиллы :-). Как водится, я готов послушать критику, но, как опять-таки водится, вряд ли она на что-то повлияет :-). Не потому что я такой упрямый (хотя поэтому тоже), а в основном потому что обычна она ...
<p>Сделал на сайте небольшой рестайлинг, который коснулся в основном <a href="http://softwaremaniacs.org/blog/">блога</a> и <a href="http://softwaremaniacs.org/forum/">форума</a>. Вспомнил заржавевшие CSS'ные скиллы :-).</p>
<p>Как водится, я готов послушать критику, но, как опять-таки водится, вряд ли она на что-то повлияет :-). Не потому что я такой упрямый (хотя поэтому тоже), а в основном потому что обычна она сводится к вкусовому восприятию, а мне больше интересна критика технологическая. Ну и кроме того, контент всегда важнее оформления.</p>
<p><a name=more></a></p>
<p>Итак вкратце, что поменялось:</p>
<ul>
<li>
<p>Метаданные урезаны до самого минимума (иногда даже слишком). В частности, наверху поста есть только ссылка на блог, в рамках которого она написана, и дата: это достаточный контекст для человека, пришедшего на статью из поисковика. В комментариях осталось только имя автора — время не добавляет ничего к пониманию смысла комментария. А бесполезный <a href="http://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%BA%D1%80%D0%BE%D0%BF%D0%BE%D1%81%D1%82">некропостинг</a> я просто удаляю.</p>
</li>
<li>
<p>Убрал правое выравнивание у кучи элементов. Это шаг к тому, чтобы свести в итоге всю вёрстку в одну колонку и выдавать её для мобильных браузеров с бо́льшим размером шрифта. Кстати, буду благодарен за ссылку на хорошую теорию о том, какими средствами это нынче делается.</p>
</li>
<li>
<p>Уменьшил шрифты заголовков и метаданных. Гигантомания ни к чему :-). Длинные заголовки топиков на форуме часто совсем плохо читались. Ещё и буквицу похоронил, которая в начале статей блога была.</p>
</li>
<li>
<p>Неожиданно вспомнил про <a href="http://www.brunildo.org/test/inline-block.html"><code>display: inline-block</code></a>, который давно поддерживают современные браузеры и применил его вместо <code>float: left</code> для выкладки блоков по горизонтали. Float'ы всегда были хаком с кучей проблем: приходилось или писать clear'ы, или вешать overflow, а inline-block всего этого лишён: просто блок целиком начинает вести себя как эдакая здоровенная буква. К нему, например, vertical-align начинает применяться. Очень удобно.</p>
</li>
</ul>
<p>Если кому-то вдруг станет интересно посмотреть в несжатые стили, то вот они: <a href="http://softwaremaniacs.org/media/style/layout-general.css">layout-general.css</a>, <a href="http://softwaremaniacs.org/media/style/layout-specific.css">layout-specific.css</a>, <a href="http://softwaremaniacs.org/media/style/fonts.css">fonts.css</a>, <a href="http://softwaremaniacs.org/media/style/colors.css">colors.css</a>, <a href="http://softwaremaniacs.org/media/style/misc.css">misc.css</a>.Дизайн API Я.ру
2012-07-30T17:51:48.203000-07:00https://softwaremaniacs.org/blog/2010/08/03/api-yaru-design/Вчера мы открыли в бету API для Я.ру. Это был первый пост в корпоративном блоге Яндекса с кодом на Питоне, что даже породило фан-арт :-). Для меня этот запуск имеет большое эмоциональное значение, потому что машиночитаемый веб — мой давний интерес, и этот проект — первый неигрушечный публичный API, где ...
<p>Вчера мы <a href="http://clubs.ya.ru/company/replies.xml?item_no=27873">открыли в бету</a> API для Я.ру. Это был первый пост в корпоративном блоге Яндекса с кодом на Питоне, что даже породило <a href="http://look4job.ya.ru/replies.xml?item_no=4522">фан-арт</a> :-). Для меня этот запуск имеет большое эмоциональное значение, потому что машиночитаемый веб — мой давний интерес, и этот проект — первый неигрушечный публичный API, где я занимаюсь дизайном, и могу смотреть, как выживают на практике теоретические соображения о том, как это должно делаться.</p>
<p>Я говорю тут от своего лица, и чтобы не возникало ложных ощущений, должен сказать, что я это всё делаю, конечно, не один. Начинал писать собственно серверную часть <a href="http://ijon-c.ya.ru/">Иван Челюбеев</a>. Моя текущая роль — проектировщик и менеджер. Код пишет сейчас <a href="http://kmerenkov.ru/">Костя Меренков</a>, а со стороны Я.рушного бэкенда нам помогает <a href="http://s-chistovich.ya.ru/">Серёжа Чистович</a>.</p>
<p>Этот пост — несколько заметок о том, как всё устроено внутри. Пишите в комментариях, если что-то нужно раскрыть подробнее.</p>
<p><a name=more></a></p>
<h2>Сервис над сервисом</h2>
<p>Сам Я.ру — многоуровневая кодобаза на нескольких языках и технологиях. Серверная её часть общается с внешним для себя миром по CORBA, и поэтому не подходит для публичного API. Кроме того, публичное API поддерживать тяжелее, чем внутреннее, из-за необходимости думать про обратную совместимость. Поэтому API сервиса Я.ру — это по сути ещё один отдельный сервис, который смотрит во внешний мир через HTTP, а внутрь — через CORBA.</p>
<p>Сервис этот написан на <a href="http://www.djangoproject.com/">Джанго</a>. Он довольно нетипичен для джанговского сервиса, потому что не использует моделей (у него вообще нет своих собственных данных), форм и шаблонов. Тем не менее, он сделан на Джанго... для простоты. Эта шокирующая идея идёт вразрез с общим трендом в тусовке модных питонистов, где принято считать Джангу "тяжёлой" (что бы это ни значило), и отказываться от неё сразу же, как только удастся найти хотя бы один компонент, который в проекте не нужен. У нас всё наоборот. Поскольку мы давно привыкли отлаживать, пакетировать, деплоить и мониторить Джанго-проекты, это делать проще, чем уживаться с любым новым фреймворком. И даже в отсутствии "более основных" компонентов сильно помогают знакомые полезные вещи: urlconf, middleware, модель запросов и ответов с вьюхами, дебагные страницы.</p>
<h2>REST</h2>
<p>Учитывая <a href="http://softwaremaniacs.org/blog/2008/11/02/rest-vs-ws/">моё отношение к REST</a>, вряд ли должно быть сюрпризом, что этот API именно такой. Если совсем честно, это было даже постановкой задачи. То есть не было так, как обычно происходит в статьях по проектированию: собрались люди и стали думать, какими средствами решать задачу "сделать API для Я.ру" и выбрали лучшую технологию для задачи. Это скучно! Мы собрались делать модный REST'овый API, а для чего именно — не суть важно :-).</p>
<h3>Ресурсы и операции</h3>
<p>Тем не менее, с какой стороны ни подходи, а я считаю, что API такого рода, как Я.ру, ложится на REST'овую идеологию почти идеально. Все ресурсы получаются совершенно естественно: люди, клубы и посты. Никакой тебе процессоорентированности или транзитивных состояний. </p>
<p>Операции над ресурсами есть очевидные, а есть не очень.</p>
<p>Очевидное — это например изменение данных профиля. Оно происходит как GET ресурса, изменение в XML нужных элементов и PUT всего профиля обратно на то же место. Здесь мы какое-то время думали над тем, чтобы поддержать обработку на входе "частичных представлений": поскольку полный профиль пользователя содержит довольно много полей, возможно клиенту было бы удобней передавать не все поля, а только те, что меняются. Тогда XML'ка могла бы выглядеть буквально как <code><person xmlns="yandex:data"><name>Новое имя</name></person></code>. Но это оказалось хлопотно на связке между API и бэкендом, и мы забросили идею. Поэтому руками составлять XML не надо, его надо считывать и менять.</p>
<p>К неочевидным операциям относятся те, которые в Я.ру делаются по сайд-эффекту создания поста: подружение/раздружение, смена настроения, присоединение к клубам. Например очевидным способом подружиться был бы POST человека (в виде URI или документа) в свою коллекцию друзей, но сейчас это делается POST'ом поста специального типа в свой фид. И хотя прямую операцию можно поддержать технически, создавая при этом пустой пост, этого делать не хочется намеренно. Потому что идеология Я.ру в том, чтобы всё интересное происходило во френдленте. А пустой пост — это не интересно, это спам.</p>
<p class=note><small>Вот прямо сейчас я понял, что это не отражено чётко в нашей документации.</small></p>
<h3>Адресуемость и навигация</h3>
<p>REST диктует, что все ресурсы должны иметь свой URI. А вот что часто упускается, так это что генерацию этих URI должен брать на себя по возможности сервер, а не клиент. То есть когда вы получаете GET'ом представление ресурса, в нём должны быть ссылки, <a href="http://code.google.com/p/uri-templates/">URI-шаблоны</a> и формы, по которым клиент понимает, куда и с чем он может обращаться дальше. На практике с этим есть две проблемы:</p>
<ul>
<li>многих людей эта идея искренне удивляет и даже пугает, все привыкли составлять URL'ы из кусочков;</li>
<li>нормальных библиотек, способных обрабатывать URI templates, мало (под Питон, кажется, <a href="http://code.google.com/p/uri-templates/wiki/Implementations">вообще нет</a>)</li>
</ul>
<p>Однако нам тут повезло, у нас все юзкейсы укладываются просто в обычные URL-ссылки. В итоге, пользуясь ярушным API, клиенту достаточно знать один входной URL: <code>https://api-yaru.yandex.ru/me/</code>, а всё остальное достижимо по возвращаемым ссылкам:</p>
<ul>
<li><code>/me/</code> редиректит на URL текущего залогиненного человека <code>/person/<uid>/</code></li>
<li><code>/person/<uid>/</code> содержит ссылки на посты, список друзей, списки клубов</li>
<li>длинные списки содержат ссылки на следующие страницы</li>
<li>из клубов есть обратные ссылки на своих членов</li>
<li>и т.д.</li>
</ul>
<p>Если вы пользуетесь этим API, и нашли, что вам приходится генерировать ссылку из кусочков — пишите, возможно мы просто упустили какую-то ссылку.</p>
<div class=note>
<p><small>Со ссылками связан один весёлый момент :-).</small></p>
<p><small>Одно время у профилей и клубов был элемент <code><id></code>, который содержал <em>число</em>. Первого же пользователя API это побудило хранить этот id в виде int'а и составлять ссылки с ним вручную. Не зная об этом, мы в какой-то момент элемент оторвали, рассудив, что идентификатор профиля и так уже есть — лежит в <code><link rel="self"></code>. Однако идея использовать длинный URL в качестве идентификатора показалась пользователям слишком радикальной, и в результате короткой битвы мы пришли к компромиссу: id вернули, но в виде короткой строчки :-).</small></p>
<p><small>Мораль в том, что глобально уникальные идентификаторы в REST-системах обязательно будут строчками, чтобы иметь возможность их расширять и комбинировать. К этому надо просто привыкнуть, и спокойно их использовать в качестве ключей.</small></p>
</div>
<h3>Форматы данных</h3>
<p>REST, опять-таки, диктует, чтобы для представлений ресурсов использовались по возможности известные стадартные форматы данных. Если так получается — это сказка. Всего лишь по одному заголовку Content-type клиент полностью понимает, как работать с этим ресурсом.</p>
<p>Поэтому для работы с блог-контентом — лентами и постами — мы выбрали <a href="http://tools.ietf.org/html/rfc5023">AtomPub</a>, который специально для этого и создавался. Этот же формат используют, например, <a href="http://code.google.com/apis/blogger/">Blogger</a> и <a href="http://codex.wordpress.org/AtomPub">WordPress</a>. Теоретически это означает, что с Я.ру должны заработать клиенты вроде <a href="http://explore.live.com/windows-live-writer">Windows Live Writer</a> и <a href="http://www.codingrobots.com/blogjet/">BlogJet</a>. Хотя конечно надо будет поработать над интероперабельностью и подпилить что-то в паре мест. Если кому интересно поотлаживать это с клиентской стороны — опять таки пишите!</p>
<p>Что интересно, что изначально, <em>неблоговый</em> контент — профили людей — тоже были выражены через AtomPub. Штука в том, что он явно позволяет расширять набор атрибутов записей. В постах мы используем это, чтобы передавать чисто ярушные атрибуты: уровень доступа и запрещённость комментариев. Но принципиально это значит, что через Atom можно выразить что угодно. В итоге довольно долго профили у нас были такими вырожденными "постами", в которых большая часть контента была кастомной.</p>
<p>Под конец, буквально за неделю до запуска, мы всё же решили, что профиль — это не пост, и особенной пользы от Atom в этом месте нет. В итоге <a href="http://clubs.ya.ru/ya/replies.xml?item_no=92391">придумали свой кастомный формат</a>. Это не очень хорошо, и я с удовольствием послушаю идеи, какие известные форматы, выражающий понятия "профиль человека" и "клуб по интересам", можно поддержать. Пока я лениво думаю над тем, чтобы выдавать профиль в формате text/x-vcard, а списки друзей в text/directory или FOAF. </p>
<hr>
<p>Что нам даёт вся эта REST'овость? Я пока не знаю. Отчасти, это как раз эксперимент, чтобы понять, какие обещания REST реально воплощаются, а каким ещё надо помогать. Например, если блог-клиенты заработают с Я.ру, я буду считать это значительным достижением.</p>
<p>Если пофантазировать, то ещё может хорошо сработать наличие глобально универсального машиночитаемого идентификатора человека. Например, в <a href="http://api.yandex.ru/fotki/">API фоток</a>, который тоже REST'овый, можно будет реализовать функциональность "добавить в любимые авторы", основываясь как раз на ярушных URI...</p>
<h2>OAuth</h2>
<p>С авторизацией мы, по сути, обогнали всех :-). Мы реализовываем драфт <a href="http://oauth.net/2/">OAuth 2.0</a>, и я специально употребляю (кривой) глагол несовершенного вида, потому что мы апдейтим реализацию под новые ревизии драфта сразу, как только они выходят. Пока разрабатывались, их сменилось уже три :-). Теперь, после публикации, обновляться будет сложнее, потому что придётся думать про обратную совместимость.</p>
<p>С точки зрения реализации это тоже отдельный сервис. Тоже написан на Джанго, но по-своему интересен, например тем, что в качестве хранилища использует только Redis. Но это, наверное, отдельная история.SubHub
2010-02-07T10:11:46-08:00https://softwaremaniacs.org/blog/2010/02/07/subhub/За выходные дописать до рабочего состояния не вышло, устал очень... Идея состоит в том, чтобы сделать персональный PSHB-хаб, который можно использовать в Джанго-проекте в связке с блогом, форумом и вообще чем угодно, что генерит новые записи. Хочется попробовать повозиться с PSHB в реальности и заодно поймать кайф от мгновенного появления ...
<p>За выходные дописать до рабочего состояния не вышло, устал очень...</p>
<p>Идея состоит в том, чтобы сделать персональный <a href="http://code.google.com/p/pubsubhubbub/">PSHB</a>-хаб, который можно использовать в Джанго-проекте в связке с блогом, форумом и вообще чем угодно, что генерит новые записи. Хочется попробовать повозиться с PSHB в реальности и заодно поймать кайф от мгновенного появления собственных записей в Гуглоридере (хотя я даже не знаю достоверно, поддерживает ли он PSHB :-) ).</p>
<p>Проект окрестил "SubHub".</p>
<p><a name=more></a></p>
<p>Название SubHub намекает на то, что это хаб, который работает только с Subscriber'ами, но не с Publisher'ами: единственный его Publisher — это ваш собственный сайт. Это упрощает дело примерно в половину, потому что не нужно реализовывать ту часть, которая отвечает за пинги от Publisher'ов. Уведомление SubHub'а о том, что что-то изменилось, происходит просто вызовом его функции в коде.</p>
<p>Сегодня я <a href="http://bazaar.launchpad.net/~isagalaev/subhub/trunk/changes">успел написать</a>:</p>
<ul>
<li>обработку запросов на подписку</li>
<li>отложенную и прямую верификацию этих запросов</li>
<li>вычищение протухших подписок</li>
</ul>
<p>И даже ещё ничего реально не тестировал. Осталось доделать:</p>
<ul>
<li>собственно рассылку нового контента подписчикам</li>
<li>хранение того, что кому ещё не отослано</li>
<li>подписывание контента HMAC-сигнатурой</li>
</ul>
<p>Если кто хочет поучаствовать, <a href="https://launchpad.net/subhub">проект открыт на Launchpad'е</a>. Для тех, кто ни одной DVCS кроме git'а не видел, вот небольшая вводная:</p>
<ol>
<li>Регистрируетесь на launchpad, закидываете туда свой публичный ключик.</li>
<li>Учите локальную машину, какой у вас логин на launchpad: <code>bzr launchpad-login username</code>.</li>
<li>Код скачивается командой <code>bzr branch lp:subhub</code>.</li>
<li>"hack-hack-hack"™.</li>
<li>Закачиваете код к себе на launchpad: <code>bzr push lp:~username/subhub/some_branch_name</code>.</li>
<li>Идёте на свою страницу с кодом <code>https://code.launchpad.net/~username</code>, выбираете бранч и жмёте там "Propose for merging".</li>
</ol>
<p>Ещё можно вообще пропустить всё связанное с launchpad'ом (у многих на него аллергия), сделать <code>bzr push</code> куда-нибудь к себе на веб-сервер и опубликовать ссылку тут в комментах.CAP-теорема Брюера
2012-01-16T21:25:27.383000-08:00https://softwaremaniacs.org/blog/2010/01/31/brewers-cap-theorem/По большей части эта статья — изложение сути статьи "Brewer's CAP Theorem" Джулиана Брауна. В оригинале много полезных ссылок и интересных примеров, поэтому если позволяет время и знание языка, почитайте его. А здесь у меня просто самая суть, покороче и по-русски. В 2000 году Эрик Брюер выдвинул гипотезу, касающуюся ключевых ...
<p>По большей части эта статья — изложение сути статьи "<a href="http://www.julianbrowne.com/article/viewer/brewers-cap-theorem">Brewer's CAP Theorem</a>" Джулиана Брауна. В оригинале много полезных ссылок и интересных примеров, поэтому если позволяет время и знание языка, почитайте его. А здесь у меня просто самая суть, покороче и по-русски.</p>
<p><a name=more></a></p>
<p>В 2000 году <a href="http://www.cs.berkeley.edu/~brewer/">Эрик Брюер</a> выдвинул гипотезу, касающуюся ключевых свойств распределённых систем, которую затем <a href="http://people.csail.mit.edu/sethg/pubs/BrewersConjecture-SigAct.pdf">доказали в MIT</a>, и с тех пор она называется теоремой Брюера или теоремой CAP (Consistency-Availability-Partition tolerance). Вольная формулировка:</p>
<p class=strong><strong>В распределённой системе невозможно обеспечить одновременное выполнение всех трёх условий: корректности, доступности, устойчивости к сбоям узлов.</strong></p>
<p>Что это за свойства.</p>
<dl>
<dt>Корректность (Consistency)</dt>
<dd><p>Говорит о том, что система всегда выдаёт только логически непротиворечивые ответы. То есть не бывает такого, что вы добавили в корзину товар, а после рефреша страницы его там не видите.</dd>
<dt>Доступность (Availability)</dt>
<dd><p>Означает, что сервис отвечает на запросы, а не выдаёт ошибки о том, что он недоступен.</dd>
<dt>Устойчивость к сбоям сети (Partition tolerance)</dt>
<dd><p>Означает, что распределённая по кластеру система работает в расчёте на случаи произвольной потери пакетов внутри сети.</dd>
</dl>
<p>Для лучшего осознания, вот три соответствующих примера того, что могут представлять собой системы без одного из этих свойств:</p>
<dl>
<dt>+Availability +Consistency -Parition tolerance</dt>
<dd><p>Система, которая рассчитывает на то, что сеть абсолютно надёжна, и благодаря этому может обеспечить консистентность данных на всех живых узлах. Или, в вырожденном случае, система из одного узла, где неконсистентности быть не может, и которая доступна всегда, когда доступен её узел. Другими словами, на практике таких систем или не существует, или они не являются распределёнными (<a href="http://softwaremaniacs.org/blog/2012/01/16/partition-tolerance/">см. также</a>).</dd>
<dt>+Consistency +Partition tolerance -Availability</dt>
<dd><p>Система с несколькими мастер-базами, которые обновляются синхронно. Она всегда корректна, потому что транзакция отрабатывает, только если изменения удалось распространить по всем серверам БД. Она продолжает корректно работать по крайней мере на чтение, если один из серверов падает. А вот попытки записи будут обрываться или сильно задерживаться, пока система не убедится в своей консистентности.</dd>
<dt>+Availability +Partition tolerance -Consistency</dt>
<dd><p>Система с несколькими серверами, каждый из которых может принимать данные, но не обязуется в тот же момент распространять их по всему кластеру. Система переживает падения части серверов, но когда они входят в строй, они будут выдавать пользователям старые данные.</dd>
</dl>
<p>Важность того, что невозможность обеспечить все три свойства сразу, доказана математически, избавляет нас (тупых инженеро́в :-) ) от тщетных попыток создать некую идеальную систему, которая <a href="http://softwaremaniacs.org/forum/django/19446/">никогда-никогда не ломается</a>. Вместо этого мы теперь можем делать привычный нам инженерный выбор двух из трёх.</p>
<p class=note><small>Как в известном предложении клиенту: "быстро, дёшево, качественно — выбирайте любые два".</small></p>
<p>Причём, как в большинстве инженерных ситуаций, этот выбор не стоит как "всё или ничего". Система может располагаться где угодно в пределах этого воображаемого треугольника, быть "более" консистентной, "менее" доступной и "слегка" нецелой. В анти-идеале можно написать систему, у которой будет плохо со всеми тремя компонентами. Уверен, вы с такими встречались :-).</p>
<p>Одно из интересных практических следствий этой теоремы в том, что в последнее время многие бизнесы решили для себя, что совсем не обязательно упираться в принципы дизайна <a href="http://ru.wikipedia.org/wiki/ACID">ACID</a>, который ставит во главу угла Consistency, жертвуя двумя другими свойствами. Теперь у нас есть такая альтернатива, как <a href="http://queue.acm.org/detail.cfm?id=1394128">BASE</a> (известная также как "eventual consistency"), которую например очень любят и используют в Amazon. Там во главе угла стоит Availability.</p>
<p>А ещё один дядька — Гай Пардон — вообще предлагает инженерное решение, которое <a href="http://guysblogspot.blogspot.com/2008/09/cap-solution-proving-brewer-wrong.html">обходит проблему CAP</a>. Правда, он немного читерствует (с математикой трудно спорить), говоря, что хоть и нельзя обеспечить все три свойства <em>одновременно</em>, можно построить систему так, чтобы она достигала желаемого постепенно.</p>
<p>P.S. <a href="http://elephantum.ya.ru/">Андрей</a>, спасибо за давешнюю ссылку на описание <a href="http://queue.acm.org/detail.cfm?id=1394128">BASE</a>.Параллелизм 2
2009-12-06T08:55:06-08:00https://softwaremaniacs.org/blog/2009/12/06/parallelism-2/Да, заголовок поста на этот раз скучный :-). Это продолжение темы, начатой в "Надо всё переписать", составленное в большей части по комментариям. Пользуясь случаем хочу сказать большое спасибо комментаторам! Написали много полезного и интересного, статья была бы неполной без этого. Терминология Как многие отметили, термины, которые я использовал, не отличаются ...
<p>Да, заголовок поста на этот раз скучный :-). Это продолжение темы, начатой в "<a href="http://softwaremaniacs.org/blog/2009/11/30/gotta-rewrite-everything/">Надо всё переписать</a>", составленное в большей части по комментариям.</p>
<p>Пользуясь случаем хочу сказать большое спасибо комментаторам! Написали много полезного и интересного, статья была бы неполной без этого.</p>
<p><a name=more></a></p>
<h2>Терминология</h2>
<p>Как многие отметили, термины, которые я использовал, не отличаются однозначностью. Попробую подробно объяснить, что я имел в виду. Я не настаиваю на своём прочтении этих терминов, они в принципе ещё совсем не устоялись.</p>
<p>Самое общее понятие — это <dfn>параллелизм</dfn>. Оно означает любое параллельное исполнение кода. Причём как "честное" — на нескольких процессорах/ядрах, так и имитируемое программно на одном процессоре. Способов его организовывать может быть несколько. Мой общий посыл относительно параллелизма как такового состоит всего-навсего в том, что теперь мы от него никуда не денемся.</p>
<p>Один из способов делать параллелизм — это <dfn>детерминированный параллелизм</dfn> (не знаю более короткого термина). Я совсем не касался его в прошлый раз, и поэтому сошлюсь на хорошо объясняющий пост "<a href="http://ghcmutterings.wordpress.com/2009/10/06/parallelism-concurrency/?ftw">Parallelism /= Concurrency</a>", из которого я понял, что речь идёт о случаях, когда у вас есть функция типа f(x, y), где x и y друг от друга не зависят, вследствие чего вы можете считать аргументы параллельно, и тогда значение f будет посчитано быстрее. Штука в том, что в императивном языке невозможно предсказать независимость x и y только на основании их аргументов, потому что каждый из этих процессов может менять состояние мира, в котором работает, и это может влиять на результат другого процесса. Зато в функциональных языках, где мир вокруг внезапно не меняется, эту параллелизацию, по идее, возможно делать автоматически.</p>
<p>Весь остальной — недетерминированный — параллелизм принято называть словом <dfn>concurrency</dfn>, которому я вменяемого перевода на русский язык не знаю. Недетерминированность concurrent-процессов происходит от того, что они обмениваются данными в непредсказумые моменты времени. Это могут быть разделяемые даные в общей памяти и может быть передача сообщений с данными между процессами. Причём как именно организованы эти процессы, не уточняется: unix-процессы, треды, процессы виртуальной машины, сервисы ядра и т.д.</p>
<p>В связи с этим стоит сказать про не очень удачное слово <dfn>тред</dfn>, под которым часто понимаются две разные вещи:</p>
<ul>
<li>Треды операционной системы или виртуальной машины, как просто способ реализации разных параллельных задач, без уточнения того, как именно их используется программа. Например: "агенты в Clojure исполняются в отдельных тредах JVM".</li>
<li>Широко распространённая модель программирования с использованием разделяемой памяти и локов для разграничения доступа к ней. Например: "когда тредов больше тысячи, всё загибается".</li>
</ul>
<p>Я очень стараюсь использовать слово "тред" только в первом смысле. Второе пусть называется "шаренные данные и локи".</p>
<p>Ещё один термин, которому не повезло — <dfn>асинхронность</dfn>. По-настоящему она означает вызов функции, которая не делает всей своей работы сразу, а быстро возвращает управление в вызвавшую программу, выполняя свои вычисления параллельно с ней. Никакого конкретного способа реализации такого параллелизма не предполагается.</p>
<p>Однако часто "асинхронность" неявно жёстко привязывают конкретно к асинхронному вводу-выводу, когда асинхронность вызова обеспечивается тем, что долгую и тупую часть операции по перекладке байтов на себя берёт отдельный сервис ядра. Чтобы отличать это понятие от "асинхронности вообще", я буду пользоваться тремином <dfn>неблокирующий IO</dfn>.</p>
<h2>Посыл</h2>
<p>Исходя из написанного выше, я теперь могу уточнить, что говоря о том, что придётся всё переписать в асинхронном стиле, я не имел в виду только неблокирующий IO. Можно без потери смысла сказать, что нам придётся всё переписать параллельно.</p>
<p>Вообще, ключевое слово там — не "асинхронно", а "придётся" :-).</p>
<h2>Разные виды concurrency</h2>
<p>Ещё одна важная вещь, которую я хочу подчеркнуть ещё раз, это то, что я считаю concurrent-программирование на императивных языках с разделяемой памятью и локами <em>неработающей</em> моделью для высокопараллельного будущего. И откровенно говоря, она и в настоящем уже достаточно проблем вызывает.</p>
<p>Именно поэтому мне интересны языки, которые реализуют concurrency по-другому.</p>
<h3>Передача сообщений</h3>
<p>В Erlang это независимые процессы с передачей сообщений между ними. Главный плюс независимости процессов — возможность запускать их на разных физических машинах, и значит масштабироваться практически бесконечно. Главный минус — потери производительности на копирование данных и на обслуживание этой самой независимости. Erlang'исты меня поправят если что, но кажется, даже для инкремента счётчика нужно запрограммировать отдельный процесс, который примет на вход текущее значение и вернёт на выходе увеличенное на единицу. Это накладно.</p>
<p>Ещё примерно тот же подход с передачей сообщений используется в Go, если я правильно читаю <a href="http://golang.org/doc/effective_go.html#concurrency">их доку по concurrency</a>. Для физической реализации параллельных процессов используются треды ОС. Причём кажется, что тред создаётся под каждую goroutine, никакого готового пула под них нет. Что не очень хорошо.</p>
<h3>Разделяемая транзакционная память</h3>
<p>В Clojure же всё наоборот. Там данные лежат в общей памяти, но никогда не изменяются. Вместо этого новая версия данных строится <em>над</em> старой, и шарит с ней все неизменённые куски. Раз данные не изменяются, доступаться к ним можно вообще без использования локов. А конфликты при записи новых данных решаются реализованной в языке software transactional memory, которая прозрачна для программиста и гарантирует корректность (хоть и недетерминированную) новых версий данных. Это очень хорошо видно в примере с эмулятором муравьёв, в <a href="http://blip.tv/file/812787">видео, на которое я уже ссылался</a>. Там в районе времени 83:45.</p>
<p>Главный плюс — это очень быстро и это наверное самый эффективный существующий способ занять все доступные ядра процессора. Кроме того, все асинхронные операции там выполняются пулом тредов, который не даёт создаваемым программистом задачам неконтроллируемо драться за процессор.</p>
<p>Главный минус — это работает только на одной машине. Соответственно, если вам нужно масштабировать систему на Clojure на несколько машин, это будут отдельные системы на Clojure, которые уже будут обмениваться данными не средствами языка, а так, как вы напишете.</p>
<h2>"Архаичный" Erlang</h2>
<p>Хорошо, я был не прав с этим словом — не пнул меня только ленивый :-). Вернее было бы сказать, что его синтаксис для меня слишком витиеват. Для питониста такое разнообразие знаков препинания (точек, точек с запятой, стрелочек и прочих дефисов) выглядит диковатым. Я точно знаю, что программировать можно без этого :-). Добавьте сюда ещё приписывание к имени функции числа аргументов при декларации их экспорта. Да и саму необходимость явно декларировать экспорт.</p>
<p>Откровенно говоря, я все эти вещи назвал "архаичными", потому что мне кажется очевидным, что в последнее время языки движутся в другую сторону: меньше церемоний, меньше синтаксиса как такового, меньше обязательных декларации чего бы то ни было. То есть по шкале выразительности языка от, скажем, Си до Питона, Erlang, придуманный в конце 80-х, таки кажется мне архаичным.</p>
<p>Впрочем, я не претендую хоть на какую-то объективность тут. Больше того, очень хорошо понимаю, что моё ворчание про Erlang из той же области, что ворчания не-лисперов про скобки, не-паскалистов про индексирование массивов с единицы, не-питонистов про отступы пробелами. Каждому — своё :-).</p>
<h2>Неэффективность веб-модели</h2>
<p>Ещё одна вещь, про которую я забыл. Я хочу не согласиться с Тимом Бреем, который считает, что на вебе проблемы с недостаточной параллелизацией обработки в целом решены. Да, у нас почти нет проблем с доступом к общим данным, и мы действительно обычно занимаем все доступные CPU, но только делаем это не особенно эффективно. Например мы плодим процессы/треды по количеству клиентов, которых пока что на обозримое будущее сильно больше, чем процессоров. А это значит, что процессы будут драться за ресурсы и мешать друг другу.</p>
<p>Представьте себе, если бы то же самое, что сейчас делают nginx и lighttpd, используя неблокирующий IO, удалось обобщить на все остальные вычисления. Веб-фреймворк мог бы принимать и держать у себя тысячи запросов пользователей, обрабатывая их задачи в пуле потоков, не превышающим количества процессоров.</p>
<p>Существующих библиотечных подходов типа питоньих eventlet и Twisted для этого не хватает. На них можно делать очень ээфективную асинхронность на неблокирующих IO-вызовах. Но к сожалению, кроме ввода-вывода существуют и другие вычислительные задачи.</p>
<hr>
<p>Ну вот... Кажется, всё должно стать яснее.Надо всё переписать
2019-03-06T00:12:43.041117-08:00https://softwaremaniacs.org/blog/2009/11/30/gotta-rewrite-everything/На днях Саймон Виллисон написал очень хороший пост про node.js. Саймону очень удаётся писать такие intro-посты про технологии, которые очень ясно передают самую суть вещей. Когда-то он так сподвиг меня взяться за изучение Джанго. Пост про node.js тоже хорошо даёт понять, почему это круто, и какое место в современной экосистеме ...
<p>На днях Саймон Виллисон написал очень <a href="http://blog.simonwillison.net/post/57956855516/node">хороший пост про node.js</a>. Саймону очень удаётся писать такие intro-посты про технологии, которые очень ясно передают самую суть вещей. Когда-то он так сподвиг меня взяться за изучение <a href="http://www.djangoproject.com/">Джанго</a>. Пост про <a href="http://nodejs.org/">node.js</a> тоже хорошо даёт понять, почему это круто, и какое место в современной экосистеме веба он занимает. Кто ещё не слышал про node.js — начинайте читать с этого поста.</p>
<p>Но мой пост не об этом. Просто, одной из своих фраз Саймон напомнил мне о моей собственной мысли, которая давно не даёт мне покоя, но пока всё никак не оформлялась:</p>
<blockquote>
<p>Node represents a clean slate. Twisted and EventMachine are hampered by the existence of a large number of blocking libraries for their respective languages.</p>
</blockquote>
<p>Это то, почему я без восторга смотрю например на <a href="http://www.tornadoweb.org/">Tornado</a> и на всякие задорные попытки запускать под ним ту же Джангу. Однако пост Саймона что-то вдруг повернул у меня в голове, и то, что раньше казалось мне проблемой, теперь кажется решением :-).</p>
<p>Впрочем, обо всём по порядку. (Осторожно, пост длинный!)</p>
<p><a name=more></a></p>
<h2>Две многозадачности</h2>
<p>Мне известны всего два принципиально разных способа делать многозадачность.</p>
<p>Самый распространённый способ — исполнение кода в виде нескольких процессов или тредов. Неудивительно, что он появился самым первым и стал распространён: большую часть забот по распараллеливанию исполнения программы берёт на себя платформа — ОС или runtime языка. Программист же пишет код в знакомом с детства однопоточном <em>синхронном</em> стиле. Да, конечно есть проблемы с передачей данных между процессами, и проблемы с общими данным в тредах, но мы их как-то с грехом пополам научились решать. По крайней мере в не очень сложных случаях.</p>
<p>Особенно комфортно с этим подходом в веб-программировании, где специфика позволяет вообще не общаться процессам друг с другом иначе как через БД. А это просто. И именно поэтому любой PHP-программист в состоянии написать <em>очень</em> многозадачную программу.</p>
<p>Проблема этого подхода — в производительности. Её, конечно, хватает сейчас почти на всё. Но начиная с какого-то момента многопроцессная система начинает кушать просто очень много памяти. Потому что, крути ни крути, эту память приходится копировать в каждый изолированный процесс (и copy-on-write до конца не спасает). Переключение на треды решает эту проблему, но снова не до конца. С ростом количества тредов система упирается теперь уже в резко возрастающее время простоя на локах, без которых синхронно в тредах программировать невозможно. Многим наверное знакомо типичное поведение веб-систем, запущенных в тредном режиме под нагрузкой: в какой-то момент она внезапно начинает сыпать 500-ми ошибками.</p>
<p>А хочется-то большего. Хочется <a href="http://www.kegel.com/c10k.html">десятков тысяч одновременных клиентов</a> и тысяч RPS.</p>
<p>С такими нагрузками справляются системы, написанные в асинхронном стиле. Как только мы отдаём IO на откуп ОС и не блокируем свой процесс, у нас высвобождается куча времени: ведь очень много веб-приложений ничего очень долгого и умного не делают, кроме того, что плюют небольшой 20-килобайтный кусочек HTML'а в сокет. И этого "довольно много" на самом деле настолько много, что мы в состоянии обработать тысячи клиентов <em>одним</em> процессом, просто бегая по ним в цикле. И поэтому тривиальные "hello-world'ы" на Tornado уделывают любой многопроцессный сервер.</p>
<h2>Проблема асинхронных систем</h2>
<p>Но счастья нет и здесь. Проблемы начинаются, когда приложению нет-нет, да и приходится делать что-то сколько-нибудь долгое. Например получить из БД довольно большой и неудобный кусок данных и перегруппировать его императивными средствами. Или ужать картинку. Или обратиться сходить по URL'у на чужой сервер. Или отослать email. Или распарсить большой XML.</p>
<p>Как только однопоточной программе, обслуживающей в цикле десять тысяч клиентов, приходится ради одного из них задержаться на, скажем, секундочку, эту секундочку ждут все остальные. Если таких "требовательных" набрался десяток — всё, ни один клиент не получит ответ раньше, чем через десять секунд. Решение не масштабируется на реальные задачи, как говорят у них в бизнесе.</p>
<p>Обойти это можно, только если любые долгие операции как-то так переписать, чтобы они не блокировали выполнение основной задачи. Причём, как именно это делать, в общем-то, понятно. Можно уводить их в отдельные процессы-обработчики, можно использовать асинхронный IO — всё это уже опробовано. Но штука в том, что тот код, которым мы пользуемся сейчас — наши любимые библиотеки, которые парсят нам XML и отсылают письма, <em>уже написаны в синхронном стиле</em>.</p>
<p>Причём нам тут в вебе ещё повезло, у нас вся архитектура строится вокруг модели коротеньких ответов, и именно поэтому нам до сих пор удавалось держать десятки и сотни RPS с помощью традиционной многопроцессности. А вот если мы посмотрим, например, в сторону десктопного софта, мы увидим, что там похожая проблема стоит ещё острее. Какое-то время назад хардварные ребята показали нам фигу и сказали, что им поднадоело издеваться над кремнием в поисках там ещё бо́льших гигагерц, и единственное, что они могут нам предложить теперь — это 2 (или 4, или 32) параллельных ядра, и если мы хотим, чтобы наши программы работали быстрее, нам придётся придумать, как их использовать.</p>
<p>Однако десктопный мир полон софта, который написан в абсолютно синхронном стиле под <em>один</em> процессор. И от того, что мы запустим, скажем, две копии каталогизатора фотографий, они не прочитают содержимое флешки с фотками в два раза быстрее (сходите проверьте, если не верите).</p>
<p>Все эти наблюдения приводят к одному интересному выводу.</p>
<p class=strong><strong>Если мы хотим, чтобы компьютеры работали значительно быстрее, нам придётся всё переписать в асинхронном стиле.</strong></p>
<p>И вот знаете, я теперь как-то думаю, что это не проблема, а как раз решение. Думаю я так, потому что кажется умные дядьки, у которых есть время и способности думать над такими вещами, нащупали то, как <em>именно</em> надо писать многозадачные программы, чтобы программисты при это оставались в здравом рассудке. Поэтому я, чёрт возьми, завидую следующим поколениями программистов. Потому что переписать что-нибудь с нуля мы все иной раз не прочь, но в этом случае у них будут на это реальные основания.</p>
<p>Вот вам прямо из головы задачка: асинхронный параллельный парсинг XML. Как мы сейчас парсим XML:</p>
<ol>
<li>Читаем данные из сокета. Как ни странно, это часть всей целиковой задачи по парсингу XML'а, просто по факту того, что в большинстве случаев собственно парсинг и приём случаются в одном и том же месте программы.</li>
<li>Разбиваем входной поток на лексемы</li>
<li>Обрабатываем их парсером.</li>
<li>Строим дерево.</li>
</ol>
<p>Жутко же неэффективно! Почему лексинг/парсинг уже полученного куска данных должен блокировать получение следующего куска? Почему бы лексинг разных кусков не делать в несколько потоков (map), которые потом объединять в единый парсинг (reduce), причём возможно тоже в несколько потоков? Зачем нам дерево, если в большинстве случаев пользователь всё равно будет на основе дерева составлять свои структуры — давайте придумаем <em>удобную</em> обработку элементов в SAX-стиле.</p>
<p>Всё это не так просто, но кажется очень интересным.</p>
<h2>Язык</h2>
<p>Я думаю, что большое значение имеет то, какой именно язык выбрать для глобального переписывания всего. Те объектные императивные языки, которыми мы с успехом пользуемся сейчас, кажется, не смогут выжить в новом мире, так же, как, например, C++ не стал мейнстримным языком веб-программирования, когда оно вылезло из коротких штанишек. Да, на Плюсах <em>можно</em> писать веб, никто не спорит. Но это просто неэффективно.</p>
<p>Мой любимый Питон по определению теряет здесь одно из своих больших преимуществ — большое количество написанных библиотек. Как раз потому, что именно их и надо все переписать. Зато его не сильно заметные <em>сейчас</em> недостатки — GIL и отсутствие полноценных анонимных блоков кода — магическим образом вырастают в размерах и вполне могут оказаться решающими.</p>
<p>Так чем тогда пользоваться? У меня в мыслях есть несколько кандидатов, каждый из которых имеет шансы стать Главным Языком Десятилетия. Какой выиграет, сказать не возьмусь, просто потому, что это зависит не только от качеств самих языков, но и просто от того, кому больше повезёт с хайпом, поддержкой и логотипом.</p>
<p>Пользуясь случаем, направляю вас заодно на цикл статей <a href="http://www.google.com/search?q=concur.next+site%3Atbray.org">Concur.next</a>, где Тим Брей обсуждает ту же языковую проблему существенно подробней. Собственно, я с ним согласен чуть менее, чем полностью, поэтому мысли мои здесь не оригинальны.</p>
<h3>Haskell</h3>
<p>Как многие говорят, функциональная природа языка — почти обязательное условие хорошей распараллеливаемости. Как только у вас есть неменяющиеся данные, вы, теоретически, избавляетесь от проблем с традиционным программированием с локами. Опять же теоретически, имея ленивые функции без сторонних эффектов, компилятор может в большой степени сам принимать решения о том, что и как можно параллелить. Я, впрочем, не большой знаток этого аспекта Haskell'а (да и остальных тоже :-)), поэтому могу тут здорово ошибаться.</p>
<p>В минусы языку записывается его большая сложность. Которая, в общем-то, убивает почти вся перспективу: если языком не можем пользоваться мы — туповатые инженеры — то писать и развивать библиотеки для реального мира становится некому.</p>
<h3>Erlang</h3>
<p>Как и Haskell, Erlang — язык функциональный. Но главное, там всё программирование происходит в терминах создания асинхронных процессов, и поэтому писать на нём асинхронные программы получается естественно.</p>
<p>Язык, однако, архаичен и странен. Кроме того, маленькое коммьюнити тоже не дают думать о том, что он когда-то прочно войдёт в мейнстрим. Впрочем, совсем помереть ему тоже не дадут, хотя бы потому, что <a href="http://couchdb.apache.org/">CouchDB</a> уже нужна всем, а написана как раз на Erlang.</p>
<h3>GCD + Си с блоками</h3>
<p>Apple придумала <a href="http://arstechnica.com/apple/reviews/2009/08/mac-os-x-10-6.ars/13">расширение к Си в виде анонимных блоков</a> и сделала работающий общесистемный пул тредов — GCD. А это ключевые компоненты асинхронного программирования. Си — низкоуровневый язык, компилируемый в нативный код, который работает очень быстро. Да и поддержка Apple стоит многого.</p>
<p>Главный же минус Си в том же, в чём и плюс — он низкоуровневый. А также совсем не функциональный, и без автоматического управления памятью. Вполне может оказаться, что на Си с блоками будет программировать одновременно и долго, и сложно, и багоопасно.</p>
<h3>Javascript</h3>
<p>Удивлены? Я не случайно начал пост со ссылки на пост про node.js. Одна из мыслей Саймона в том, что Javascript совсем неплох, как язык для асинхронного программирования. Анонимные блоки кода там есть. Да и не отнять, что сейчас весь ajax пишется на Javascript именно в асинхронном стиле.</p>
<p>Минусы — та же императивность и, что наверное ещё хуже, отсутствие модульности. Если что и губит этот хороший язык прямо сейчас — это общее глобальное пространство имён и отсутствие стандартного способа сделать импорт библиотеки.</p>
<h3>Clojure</h3>
<p>И вот оно — моё любимое слово последней недели (кстати, по-русски я её как-то сразу стал называть "кложура" с ударением на последний слог). Про Clojure я <a href="http://www.tbray.org/ongoing/When/200x/2009/11/18/Clojure-Parallel-I-O">узнал у того же Тима Брея</a>. Потом пошёл на сайт, посмотрел на несколько выступлений Рича Хики об этом языке, и всё — по уши в него влюблён. (В <em>язык</em>, не в Рича :-) ).</p>
<p>Clojure — это Лисп. И для меня это плюс. Когда-то я лиспами заинтересовался, и стал тем, кого "скобочки не напрягают", мне очень нравится идея code-as-data. Хотя я понимаю, что это не для всех, но вы, ребята, сами виноваты :-).</p>
<p>Однако, уж если я вменил архаичность Erlang'у, то про Лиспы можно сказать то же самое, если не в большей степени. Но Clojure — это <em>современный</em> Лисп. Там есть не только списки, но и например нативные векторы и словари (задаваемые литерально фигурными скобками). Там нет проблем с юникодом (о чём чуть позже), и там много удобных макросов. То есть это Лисп, который придумали недавно, а не в конце пятидесятых.</p>
<p>Главная же фишка Clojure — это ориентация на многопоточность. Все структуры данных там неизменяемы (да, словари тоже), что даёт возможность распараллеливать доступ к ним вообще без локов. Эти неизменяемые структуры данных умеют очень дёшево дублироваться без использования полного копирования, что делает их быстрыми. И в языке есть несколько синтаксических конструкций, позволяющих явно задавать асинхронное выполнение и транзакции для связанных изменений. Всё это обязательно стоит послушать и посмотреть в оригинале в <a href="http://blip.tv/file/812787">выступлении Рича Хики про параллельность</a>. Оно длинное, но того стоит — взрывает мозг напрочь!</p>
<p>Хотя есть у Clojure и субъективный минус: она реализована на JVM. С одной стороны это очень практично: куча библиотек, готовая инфраструктура тредов, сборки мусора и JIT-компиляции. Очень правильный юникод, опять же. Но у меня ко всей этой инфраструктуре есть субъективная нелюбовь. Несмотря на то, что JVM уже много лет, она всё ещё вызывает проблемы при установке, всё ещё занимает немеряно ресурсов, и GUI-приложения на ней всё ещё не выглядят адекватно ни в одной десктопной среде (или я ошибаюсь?). Впрочем с практической стороны это всё не так страшно, если вести речь о веб-приложениях. Там все эти потроха пользователям не видны.</p>
<p>Так что официально заявляю, что в будущей гонке за процессорное время мой фаворит — Clojure.</p>
<h2>P.S.</h2>
<p>Забавно поворачивается история, однако. Когда-то, когда Windows 95 пришла на смену Windows 3.11, одним из главных преимуществ новой архитектуры было то, что вместо кооперативной многозадачности, когда каждое приложение должно было само давать работать другим, применялась вытесняющая многозадачность, когда время на процессы жёстко распределялось системой.</p>
<p>А теперь посмотрите, куда мы пришли. Выясняется, что внешняя система не в состоянии рулить своими процессами максимально эффективно, не зная их семантики. Хочешь независимого исполнения — делай копии всей памяти на каждый процесс, даже если каждому из них нужны лишь мизерные её доли. Жалко памяти — обвешивай доступ к любым системным объектам локами. И не получится оптимизировать раздачу времени процессам так, чтобы они по возможности использовали независимые ресурсы системы. Потому что для этого надо знать, что конкретно процессы собираются сейчас делать.</p>
<p>Поэтому мы снова возвращаемся к тому, что процессы сами должны управлять своим распараллеливанием. А это — та самая кооперативная многозадачность. Хотя, конечно, существенно более сложная и безопасная, чем в Windows 3.11, и с другими языковыми средствами. Но забавно :-).PubSubHubbub
2009-11-12T05:47:42-08:00https://softwaremaniacs.org/blog/2009/11/12/pubsubhubbub/У нас в Яндексе вчера был Бретт Слаткин и мы тёплой компанией инженеров общались про его с Брэдом Фицпатриком детище — PubSubHubbub. Кто не знает, что такое PubSubHubbub — сходите на страницу проекта, там очень понятная презенташка, лучше и короче не расскажешь. Вещь очень перспективная, как по мне. А после ...
<p>У нас в Яндексе вчера был <a href="http://www.google.com/profiles/bslatkin">Бретт Слаткин</a> и мы тёплой компанией инженеров общались про его с <a href="http://bradfitz.com/">Брэдом Фицпатриком</a> детище — <a href="http://code.google.com/p/pubsubhubbub/">PubSubHubbub</a>.</p>
<p><a name=more></a></p>
<p>Кто не знает, что такое PubSubHubbub — сходите на <a href="http://code.google.com/p/pubsubhubbub/">страницу проекта</a>, там очень понятная презенташка, лучше и короче не расскажешь. Вещь очень перспективная, как по мне. А после общения с Бреттом я воодушевился технологией ещё больше, потому что ребята придумывают её из очень правильных предпосылок. Он подчеркнул, что простота и практичность ценятся больше теоретической чистоты, и для них пока главное — решить конкретную задачу быстрого обновления фидов, заменив периодический poll на более эффективный push.</p>
<p>Изюминка в том, что <em>этот</em> push отличается от всех предыдущих fail'ов как раз тем, что делается инженерами, и архитектура у него соответствующая (HTTP, слабая связность, отсутствие необходимости в дремучих тулзах).</p>
<p>Вот несколько лично мне запомнившихся хайлайтов из разговора:</p>
<ul>
<li>Между собой они называют протокол "Hubbub" или "PSHB", потому что полное название трудно говорить быстро.</li>
<li>Рефрен про то, что они стараются "make things a bit less awful" удивительно совпал с моей собственной подписью в рабочих email'ах "Making things suck less".</li>
<li>Сокращение "WS-*" произносится как "WS-crap", очень позабавило :-).</li>
<li>Впечатлила демка с FriendFeed'ом, который показывает в браузере новый пост в ленте через примерно 1.5 секунды после его написания в совершенно другом месте. </li>
<li>Да, FriendFeed оказывается поддерживает hubbub, и реализация заняла у них что-то типа одного дня.</li>
<li>Идея о том, что какой-нибудь блогохостинг, получая новый пост, может <em>сначала</em> разослать апдейты всем получателям, а потом только заняться неспешным складыванием его в БД, экономя на том, чтобы их потом оттуда сразу же вынимать, действительно "hits over the head", как Бретт и обещал. Очень похоже на write-through кеш.</li>
<li>Как и ожидалось от инженера, ответ на вопрос про бизнес-модель для публичных хабов был куда менее конкретным, чем на все технические вопросы. Кому это интересно, действительно? :-)</li>
</ul>
<p>В практическом плане я, кажется, воодушевился реализовать на sm.org персональный хаб для блога (как руки дойдут™). Будем ли мы что-то делать в рамках Яндекса, пока не знаю. Это всё пока требует внутреннего технического евангелизма. Но мне очень хочется :-).