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

<channel>
	<title>De geek à directeur technique</title>
	<atom:link href="http://www.geek-directeur-technique.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.geek-directeur-technique.com</link>
	<description>Le blog d&#039;un geek devenu directeur technique</description>
	<lastBuildDate>Mon, 20 Feb 2012 17:13:16 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>Les langages de programmation &#8211; Partie 4 : simplicité et syntaxe</title>
		<link>http://www.geek-directeur-technique.com/2012/01/26/les-langages-de-programmation-partie-4-simplicite-et-syntaxe</link>
		<comments>http://www.geek-directeur-technique.com/2012/01/26/les-langages-de-programmation-partie-4-simplicite-et-syntaxe#comments</comments>
		<pubDate>Thu, 26 Jan 2012 09:24:05 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Questions techniques]]></category>
		<category><![CDATA[basic]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Pascal]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=1064</guid>
		<description><![CDATA[Dans la suite de mes trois précédents articles consacrés à ce sujet, j&#8217;ai commencé à écrire un très long article dans lequel je décortique point par point les différentes caractéristiques des langages de programmation. L&#8217;écriture de l&#8217;article m&#8217;a obligée à structurer mes idées, et m&#8217;a aidée à réaliser un certain nombre de choses. Par contre, [...]]]></description>
			<content:encoded><![CDATA[<p>Dans la suite de mes trois précédents articles consacrés à ce sujet, j&#8217;ai commencé à écrire un très long article dans lequel je décortique point par point les différentes caractéristiques des langages de programmation. L&#8217;écriture de l&#8217;article m&#8217;a obligée à structurer mes idées, et m&#8217;a aidée à réaliser un certain nombre de choses. Par contre, l&#8217;article lui-même est devenu un long truc un peu indigeste, alors j&#8217;ai décidé de le mettre à la poubelle.</p>
<p>Pour le moment, je vais juste reprendre une partie de ce que j&#8217;avais écrit, concernant la syntaxe des langages.</p>
<p>Une des caractéristiques essentielles d&#8217;un langage de programmation, c&#8217;est d&#8217;être facile à lire et à relire. On doit pouvoir lire du code source comme un linguiste peut lire un texte écrit dans une langue étrangère.<br />
Cela passe notamment par un syntaxe légère, qui ne se mette pas en travers de la lecture, qui ne soit pas inutilement verbeuse et qui reste sans ambiguïté.</p>
<h3>Blocs, labels, accolades et indentation</h3>
<p>Pour illustrer mon propos, je vais vous montrer comment on code la <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Factorielle" target="_blank">factorielle</a> récursive dans plusieurs langages procéduraux. Tout d&#8217;abord en <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Pascal_(langage)" target="_blank">Pascal</a> :</p>
<pre>FUNCTION factorielle (n: shortint)&nbsp;: integer;
BEGIN
 &nbsp;  IF n &lt;= 1 THEN
        BEGIN
            WRITELN('End of loop');
 &nbsp; &nbsp; &nbsp;      factorielle&nbsp;:= 1
        END
 &nbsp;  ELSE
        BEGIN
            WRITELN('Loop');
 &nbsp; &nbsp; &nbsp;      factorielle&nbsp;:= n * factorielle (n - 1)
        END;
END;</pre>
<p>Ensuite en <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/BASIC" target="_blank">BASIC</a> (<a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Visual_Basic_.NET" target="_blank">VB.net</a>, inspiré par le site <a title="Developpez.com" href="http://plasserre.developpez.com/ve1-2.htm" target="_blank">Developpez.com</a>,&nbsp;© Philippe Laserre)&nbsp;:</p>
<pre>Function Factorielle (ByVal N as Long) As Long
 &nbsp;  If N=1 Then
 &nbsp; &nbsp; &nbsp;  Console.WriteLine("End of loop")
 &nbsp; &nbsp; &nbsp;  Return 1
 &nbsp;  End If
 &nbsp;  Console.WriteLine("Loop")
 &nbsp;  Return N * Factorielle(N - 1)
End Function</pre>
<p>Et voici le code équivalent en <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/C_(langage)" target="_blank">C</a> :</p>
<pre>int factorielle(int n)
{
 &nbsp;  if (n &lt;= 1)
    {
        printf("End of loop\n");
 &nbsp; &nbsp; &nbsp;  return 1;
    }
    printf("Loop\n");
 &nbsp;  return n * factorielle(n - 1);
}</pre>
<div>
<p>Pour terminer cette démonstration, voici la même fonction factorielle en <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Python_(langage)#Syntaxe" target="_blank">Python</a> :</p>
<pre>def factorielle(x):
 &nbsp;  if x &lt;= 1:
 &nbsp; &nbsp; &nbsp;  print("End of loop\n")
 &nbsp; &nbsp; &nbsp;  return 1
 &nbsp;  print("Loop\n")
 &nbsp;  return x * factorielle(x - 1)</pre>
<p>Je vous laisse voir le code équivalent en <a title="Ruby France" href="http://www.rubyfrance.org/documentations/les-bases/le-guide-de-lutilisateur/des-exemples-simples/" target="_blank">Ruby</a>, qui conjugue syntaxe légères et <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Type_(informatique)#Typage" target="_blank">typage dynamique</a> comme le Python.</p>
<p>Je crois que j&#8217;ai déjà cité mon prof de C en prépa, qui parlait des différences entre les langages créés par des mathématiciens suisses (cf. le Pascal) et ceux créés par des hippies californiens (cf. le C). Et encore, l&#8217;exemple serait plus criant si il fallait déclarer des variables&nbsp;; en Pascal c&#8217;est vraiment marrant.</p>
<p>Je vous rappelle que le BASIC est né en 1963, le Pascal en 1970, le C en 1972, le Python en 1990 et le Ruby en 1995. Il y a forcément une notion de modernisme dans la simplification de la syntaxe des langages.</p>
<p>Je ne parle pas de la compacité d&#8217;un code par rapport à l&#8217;autre, mais bien de sa lisibilité intrinsèque. Le C utilise des accolades, là où le Pascal et le BASIC utilisent du texte (BEGIN, END, End If, End Function). Savoir où placer les point-virgules en fin de ligne n&#8217;est pas forcément naturel au premier abord en Pascal, alors qu&#8217;en C le comportement est consistant&nbsp;; le BASIC et le Python se suppriment quant à eux complètement cette contrainte. Pour finir, la déclaration de la fonction avec son type de retour et son paramètre, est assez similaire entre le Pascal et le BASIC&nbsp;; le C est bien moins verbeux. En Python, il n&#8217;y a pas de déclaration du type des fonctions et des variables, c&#8217;est encore plus simple à lire (mais moins rigoureux diront certains).</p>
<p>Sur un exemple comme celui-ci, mon avis est assez évident. Le fait d&#8217;utiliser des termes textuels peut sembler plus facile à appréhender pour les débutants, mais j&#8217;y apporte deux objections&nbsp;: Premièrement, même si on ne vous explique pas le rôle des accolades, le code en C ci-dessus est très facilement compréhensible, et on devine sans peine à quoi elles servent. Deuxièmement, les éléments de syntaxe basique sont intégrés très rapidement par les codeurs débutants&nbsp;; obliger les développeurs à affronter la pollution visuelle des BEGIN/END pendant tout le reste de leur vie me semble être un mauvais calcul.</p>
<p>Je ne veux pas tant montrer que je préfère la syntaxe du C que de pointer le genre de détails qui font qu&#8217;un langage peut − ou non − mettre des bâtons dans les roues des développeurs qui l&#8217;utilisent.</p>
<p>On peut remarquer que le Python va encore plus loin en supprimant même les accolades. C&#8217;est l&#8217;indentation qui détermine les blocs de code. Les experts du langage trouvent ça très positif&nbsp;: le code est plus simple car il ne nécessite plus aucune indication de début et fin de bloc, et cela unifie le formatage du code. Même si je suis sensible à ces arguments, j&#8217;y vois personnellement 3 inconvénients&nbsp;: le mélange d&#8217;espaces et de tabulations peut générer des bugs indémerdables, à plus forte raison quand plusieurs développeurs interviennent en utilisant des éditeurs de texte différents&nbsp;; il n&#8217;y a rien pour “fermer” les blocs, on a l&#8217;impression que les fonctions flottent dans le vide (c&#8217;est une question d&#8217;habitude, je sais). Mais surtout, dans certains cas, on se retrouve à quand même utiliser des symboles de début et fin de bloc, et à ce moment-là l&#8217;indentation arrête d&#8217;être significative&nbsp;; par exemple, quand on construit une liste, le code suivant est fonctionnel, complètement logique, mais brise l&#8217;indentation habituelle en Python&nbsp;:</p>
<pre>ma_liste = [
 &nbsp; &nbsp; &nbsp; "aaa",
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "bbb",
  "ccc"
]</pre>
<h3>Dollars et points-virgules</h3>
<p>Les deux autres notions basiques sont l&#8217;utilisation du caractère dollar ($) devant les noms de variables et les points-virgules en fin d&#8217;expression.</p>
<p>Historiquement, le dollar était inutile dans les langages compilés (Pascal, C), mais traditionnel dans les langages de script (shell, Perl). C&#8217;est assez amusant, quand je code en C je me passe très bien des dollars, alors qu&#8217;en PHP je trouve ça naturel. C&#8217;est clairement une question d&#8217;habitude. J&#8217;ai tendance à trouver les dollars pratiques pour différencier d&#8217;un seul coup d&#8217;œil les variables des autres constructions du langage, comme les namespaces, ce qui peut s&#8217;avérer utile au fur et à mesure de l&#8217;ajout de fonctionnalités dans les langages modernes.</p>
<p>Les points-virgules en fin d&#8217;expression m&#8217;ont toujours semblés tout aussi naturels. Ils permettent de savoir où se termine l&#8217;expression. La plupart du temps il n&#8217;y en a pas besoin, car les expressions sont courtes et faciles à lire. Mais parfois − pas si rarement que ça, en fait − on écrit des expressions sur plusieurs lignes, et mon œil cherche instinctivement le point-virgule pour savoir où est la fin.<br />
On peut remarquer que le <a href="http://golang.org" target="_blank">Go</a> (nouveau langage de Google) n&#8217;a pas besoin de point-virgule en fin de ligne&nbsp;; mais, pour éviter les ambiguïtés, il impose que les accolades ouvrantes soient sur la même ligne que l&#8217;instruction <em>if</em> qui précède (cf. <a href="http://golang.org/doc/go_tutorial.html#tmp_33" target="_blank">documentation</a>). On en arrive donc à une situation très étrange, où pour simplifier l&#8217;écriture on se retrouve à la contraindre&#8230;</p>
<p>Un dernier point de détail concernant la syntaxe&nbsp;: L&#8217;utilisation des parenthèses.<br />
Plusieurs langages n&#8217;obligent pas de mettre des parenthèses autour des paramètres d&#8217;une fonction. Moi, ça me gêne. Depuis le collège, on est habitué à écrire des fonctions mathématiques&nbsp;; quand on écrit <em>f(x)</em>, on ne se pose aucune question, on sait que <em>f</em> est une fonction et qu&#8217;elle prend un paramètre nommé <em>x</em>.</p>
<h3>Parenthèses</h3>
<p>Idem pour les parenthèses autour de la condition d&#8217;un <em>if</em> ou d&#8217;un <em>while</em>. Je trouve que ça donne une délimitation visuelle claire. Si on regarde l&#8217;exemple suivant (tiré de la documentation du Go)&nbsp;:</p>
<pre>for i:= 0; i &lt; flag.NArg(); i++ {
 &nbsp;  if i &gt; 0 {
 &nbsp; &nbsp; &nbsp;  s+= space
 &nbsp;  }
 &nbsp;  s+= flag.Arg(i)
}</pre>
<p>Je le trouve moins lisible que s&#8217;il s&#8217;écrivait&nbsp;:</p>
<pre>for (i = 0; i &lt; flag.NArg(); i++) {
 &nbsp;  if (i &gt; 0)
 &nbsp; &nbsp; &nbsp;  s += space;
 &nbsp;  s += flag.Arg(i);
}</pre>
<p>Mais c&#8217;est sûrement encore une question d&#8217;habitude.</p>
<h3>Conclusion</h3>
<p>Bon&#8230; Encore un article qui ne sert pas à grand-chose&#8230;<br />
Tout ça pour dire que je suis habitué à un style de syntaxe auquel tout le monde est habitué, qui constitue les bases du C, du C++, du Perl, du Javascript, du PHP, &#8230;</p>
<p>J&#8217;ai un peu l&#8217;impression qu&#8217;après une période durant laquelle l&#8217;informatique s&#8217;est rapidement complexifiée avec l&#8217;arrivée des premiers gros ordinateurs, la syntaxe des langages a été simplifiée (merci <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Dennis_Ritchie" target="_blank">Dennis Ritchie</a>). Sur un cycle de re-complexification, on a abouti au C++, qui est quand même un monument à ce niveau. On a re-simplifié par la suite de différentes manières, plus ou moins réussies (Python, Lua, Java, Ruby, PHP, &#8230;).</p>
<p>Les être humains répétant régulièrement leurs erreurs, je pense que nous sommes actuellement dans un nouveau cycle de complexification. Il parait important d&#8217;ajouter des fonctionnalités aux langages, alors que ces mêmes fonctionnalités pourraient rester dans des bibliothèques externes&nbsp;; il semble nécessaire de farcir les frameworks de capacités supplémentaires, alors que les systèmes de plugins qui sont là pour ça. On ajoute plein de choses dans les systèmes d&#8217;exploitation, pour finalement <a title="PC Inpact" href="http://www.pcinpact.com/news/65434-windows-8-metro-interface-bureau.htm" target="_blank">leur donner la simplicité des OS mobiles</a>&#8230;</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2012/01/26/les-langages-de-programmation-partie-4-simplicite-et-syntaxe/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Professionnalisme : Le (mauvais) exemple de Fetchnotes</title>
		<link>http://www.geek-directeur-technique.com/2012/01/24/professionnalisme-le-mauvais-exemple-de-fetchnotes</link>
		<comments>http://www.geek-directeur-technique.com/2012/01/24/professionnalisme-le-mauvais-exemple-de-fetchnotes#comments</comments>
		<pubDate>Tue, 24 Jan 2012 12:38:33 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Organisation personnelle]]></category>
		<category><![CDATA[bévues]]></category>
		<category><![CDATA[email]]></category>
		<category><![CDATA[Fetchnotes]]></category>
		<category><![CDATA[professionnalisme]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=1075</guid>
		<description><![CDATA[Cette nuit, le site Fetchnotes a procédé à des tests durant lesquels toutes les personnes inscrites à leur beta ont reçu l&#8217;email suivant, intitulé &#171;&#160;Test&#160;&#187;&#160;: This is my test bitches Évidemment, 45 minutes plus tard, ils envoyaient un message d&#8217;excuse, intitulé &#171;&#160;We Really Screwed Up&#160;&#187;&#160;: Hi, From all of us on the Fetchnotes team, we [...]]]></description>
			<content:encoded><![CDATA[<p>Cette nuit, le site <a href="http://www.fetchnotes.com/" target="_blank">Fetchnotes</a> a procédé à des tests durant lesquels toutes les personnes inscrites à leur beta ont reçu l&#8217;email suivant, intitulé &laquo;&nbsp;<strong>Test</strong>&nbsp;&raquo;&nbsp;:</p>
<pre>This is my

test

bitches</pre>
<p>Évidemment, 45 minutes plus tard, ils envoyaient un message d&#8217;excuse, intitulé &laquo;&nbsp;<strong>We Really Screwed Up</strong>&nbsp;&raquo;&nbsp;:</p>
<pre>Hi,

From all of us on the Fetchnotes team, we sincerely apologize for
the email you received earlier. We were working on a new email
system and unfortunately one of our test emails, with some
questionable language, slipped through. We are deeply sorry for
what happened and hope that we can continue striving to deliver an
excellent experience. This was just not one of those experiences
unfortunately.

Thank you,

The Fetchnotes Team</pre>
<p>Ouaip, ils se sont vautrés comme des merdes. Non seulement ils ont envoyé un email de test à tous leurs abonnés, mais le message en question peut être très mal perçu (ce sont leurs abonnés, qu&#8217;ils traitent de salopes&nbsp;?).</p>
<p>C&#8217;est le genre de grosses connerie qu&#8217;on redoute tous de laisser s&#8217;échapper un jour. En tant qu&#8217;informaticien, je comprends comment ce genre de chose peut arriver. Mais ça ne donne vraiment pas une bonne image de professionnalisme.</p>
<p>J&#8217;ai appris &laquo;&nbsp;à la dure&nbsp;&raquo; que le professionnalisme est une attitude à laquelle il faut prêter attention en toute circonstance. Lors de mon tout premier stage en entreprise, quand j&#8217;étais jeune et bête, je me suis retrouvé à devoir faire une maquette de page web, pour en faire ensuite l&#8217;intégration. Je ne suis pas graphiste, je ne connaissais pas encore les générateurs de Lorem Ipsum&nbsp;; il a donc fallu que je remplisse la maquette avec des textes à la con. Et j&#8217;ai vraiment écrit des trucs à la con&nbsp;; notamment que le patron de l&#8217;entreprise était poursuivi pour malversations financières&#8230;</p>
<p>Cette maquette, qui ne devait être utilisée que par un autre développeur et moi, a été utilisée par le chef de projet. Sans m&#8217;en informer (pourquoi aurait-il dû&nbsp;?), il l&#8217;a placée dans un document de présentation qu&#8217;il a envoyé au client.<br />
Quand je me suis rendu compte de ça, je me suis senti tout pâle. J&#8217;ai couru voir mon chef de projet, et on s&#8217;est heureusement rendu compte qu&#8217;il avait réduit l&#8217;image au point que le texte était illisible.</p>
<p>Que ce serait-il passé si le client avait lu mes textes débiles&nbsp;? J&#8217;aurais pu me faire virer de mon stage. Au minimum.</p>
<p>Cela m&#8217;a servi de leçon. Désormais, je fais toujours attention à ce que j&#8217;écris. Quelle que soit la situation, je me dis que ce que je produis pourrait un jour être propagé en dehors du périmètre dans lequel je le crois confiné.</p>
<p>Vous ne savez pas le nombre de fois où j&#8217;ai vu des échanges internes, à propos d&#8217;un projet par exemple, être transférés à un client ou un partenaire. Simplement parce qu&#8217;au milieu des échanges s&#8217;est retrouvée une information qui pouvait intéresser le client, quelqu&#8217;un lui envoie. Sans se rendre compte que c&#8217;est l&#8217;ensemble de la discussion qui a été transférée, y compris certains messages très peu professionnels voire même certaines remarques diplomatiquement difficiles à justifier vis-à-vis du client.</p>
<p>Un simple “Projet de merde&nbsp;!”, lâché par un développeur un jour de stress, risque de se retrouvé glissé dans une discussion&nbsp;; à force de &laquo;&nbsp;reply-to&nbsp;&raquo;, le texte est copié mais on n&#8217;y prend pas garde, jusqu&#8217;à ce que le client exprime son mécontentement et réclame une explication.</p>
<p>Si vous ne voulez pas vous retrouver dans ce genre de situation, dites-vous que vos échanges d&#8217;emails professionnels doivent toujours refléter votre professionnalisme.</p>
<p>Pour en revenir à Fetchnotes, ils ont cumulé les bévues. Envoyer un email de test à toute leur base de données, c&#8217;est con. Envoyer un email débile et limite insultant, c&#8217;est très con. J&#8217;espère pour eux que cela ne les empêchera pas de mener leur service avec succès&nbsp;; et j&#8217;espère que le jeune développeur qui fait ses tests en écrivant des bêtises a été vacciné à vie contre ce genre de choses − comme je l&#8217;ai moi-même été, avec moins de conséquence.</p>
<p>Juste pour terminer sur le sujet&nbsp;: Quand on fait une connerie, il faut l&#8217;assumer. Fetchnotes l&#8217;a bien compris, menant une communication rapide et efficace, par email et sur Twitter. Ils s&#8217;excusent, reconnaissent leur erreur (faite par leur cofondateur), répondent individuellement à tous les messages sur le sujet. <strong>Bien joué</strong>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2012/01/24/professionnalisme-le-mauvais-exemple-de-fetchnotes/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Les langages de programmation &#8211; Partie 3 : Créer un interpréteur (TinyCC inside)</title>
		<link>http://www.geek-directeur-technique.com/2012/01/11/les-langages-de-programmation-partie-3-creer-un-interpreteur-tinycc-inside</link>
		<comments>http://www.geek-directeur-technique.com/2012/01/11/les-langages-de-programmation-partie-3-creer-un-interpreteur-tinycc-inside#comments</comments>
		<pubDate>Wed, 11 Jan 2012 01:14:47 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Questions techniques]]></category>
		<category><![CDATA[bytecode]]></category>
		<category><![CDATA[compilateur]]></category>
		<category><![CDATA[interpréteur]]></category>
		<category><![CDATA[machine virtuelle]]></category>
		<category><![CDATA[TinyCC]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=1034</guid>
		<description><![CDATA[J&#8217;avais annoncé initialement que j&#8217;écrirais 3 articles sur le sujet des langages de programmation, et que le troisième serait consacré au thème &#171;&#160;Qu&#8217;est-ce que j&#8217;aimerais avoir comme langage&#160;&#187;. Bon, j&#8217;ai menti, il y aura finalement 4 articles, et celui-ci sera consacré à la création d&#8217;un langage de programmation interprété. Je ne vais pas m&#8217;étendre sur [...]]]></description>
			<content:encoded><![CDATA[<p><a title="Les langages de programmation – Partie 1&nbsp;: Ce que je connais" href="http://www.geek-directeur-technique.com/2012/01/06/les-langages-de-programmation-partie-1-ce-que-je-connais">J&#8217;avais annoncé initialement</a> que j&#8217;écrirais 3 articles sur le sujet des langages de programmation, et que le troisième serait consacré au thème &laquo;&nbsp;Qu&#8217;est-ce que j&#8217;aimerais avoir comme langage&nbsp;&raquo;.<br />
Bon, j&#8217;ai menti, il y aura finalement 4 articles, et celui-ci sera consacré à la création d&#8217;un langage de programmation interprété.</p>
<p>Je ne vais pas m&#8217;étendre sur la théorie de la création des <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Compilateur" target="_blank">compilateurs</a> et des <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Interpr%C3%A8te_(informatique)" target="_blank">interpréteurs</a>. J&#8217;avais lu un <a title="Amazon" href="http://www.amazon.fr/gp/product/2225846154/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;tag=degeekadirect-21&amp;linkCode=as2&amp;camp=1642&amp;creative=6746&amp;creativeASIN=2225846154" target="_blank">énorme bouquin</a> sur le sujet il y a quelques années&nbsp;; c&#8217;était super intéressant, mais j&#8217;avoue ne pas en avoir retenu grand chose de concret. Il faut dire que c&#8217;est assez éloigné de mes préoccupations quotidiennes.</p>
<p>Non, je vais plutôt vous parler&#8230; d&#8217;un moyen de contourner un peu tout ça.</p>
<h3>Compilateurs et interpréteurs</h3>
<p>Reprenons. Si on simplifie <em>à l’extrême</em>, le travail d&#8217;un compilateur c&#8217;est ça&nbsp;:</p>
<ol>
<li>Lecture du code source.</li>
<li>Analyses diverses.</li>
<li>Construction de l&#8217;<a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Arbre_syntaxique" target="_blank">arbre de compilation</a>.</li>
<li>Optimisation.</li>
<li>Génération du code machine et écriture du fichier exécutable.</li>
</ol>
<p>Le boulot d&#8217;un interpréteur, c&#8217;est plutôt&nbsp;:</p>
<ol>
<li>Lecture du code source.</li>
<li>Analyses diverses.</li>
<li>Construction de l&#8217;arbre d&#8217;interprétation.</li>
<li>Optimisations éventuelles.</li>
<li>Exécution (évaluation de l&#8217;arbre).</li>
</ol>
<p>Dans le principe, il y a beaucoup de similitudes. D&#8217;ailleurs, toutes les explications données dans le livre dont je vous parlais plus haut étaient dans l&#8217;optique de construire un interpréteur&nbsp;; alors que le livre s&#8217;intitulait <em><a title="Amazon" href="http://www.amazon.fr/gp/product/2225846154/ref=as_li_qf_sp_asin_tl?ie=UTF8&amp;tag=degeekadirect-21&amp;linkCode=as2&amp;camp=1642&amp;creative=6746&amp;creativeASIN=2225846154" target="_blank">Les compilateurs</a></em>.</p>
<p>De plus en plus, les interpréteurs passent par un code machine intermédiaire, le <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Bytecode" target="_blank">bytecode</a>, destiné à être exécuté par une <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Machine_virtuelle" target="_blank">machine virtuelle</a>. Ce code peut parfois être stocké sur disque pour accélérer les exécutions futures, mais ce n&#8217;est pas systématique. Cela donne donc des interpréteurs qui fonctionnent ainsi&nbsp;:</p>
<ol>
<li>Génération du bytecode</li>
<ol>
<li>Lecture du code source.</li>
<li>Analyses diverses.</li>
<li>Construction de l&#8217;arbre de compilation.</li>
<li>Optimisation.</li>
<li>Génération du bytecode.</li>
</ol>
<li>Exécution</li>
<ol>
<li>Chargement de la machine virtuelle.</li>
<li>Exécution (évaluation du bytecode par la machine virtuelle).</li>
</ol>
</ol>
<p>Ce fonctionnement est censé être plus rapide, car le bytecode est généré spécifiquement pour la machine virtuelle&nbsp;; celle-ci l&#8217;exécute comme du code natif à ses yeux, et non pas comme une évaluation pas-à-pas de l&#8217;arbre syntaxique (oh les explications foireuses&nbsp;! je m&#8217;en excuse platement).</p>
<p>Il existe un autre type d&#8217;interpréteur, les <a title="Wikipedia" href="http://en.wikipedia.org/wiki/Just-in-time_compilation" target="_blank">compilateurs JIT</a>. Leur principe est de compiler à la volée le code source en binaire natif. Le gros intérêt est que le binaire est exécuté directement par l&#8217;ordinateur, il n&#8217;a pas besoin de machine virtuelle. Pourquoi tous les interpréteurs ne sont pas des compilateurs JIT&nbsp;? Parce que c&#8217;est loin d&#8217;être simple à mettre en œuvre. Les compilateurs natifs sont habituellement assez lents&nbsp;; cela ne pose pas le moindre problème, car quand on compile du code C ou C++, le but est d&#8217;obtenir un programme dont l&#8217;exécution sera la plus rapide possible&nbsp;; ils intègrent d&#8217;ailleurs plein d&#8217;optimisations qui ralentissent la compilation mais qui accélèrent l&#8217;exécution.</p>
<p>Un compilateur JIT doit donc être capable de compiler nativement dans un délai très court, afin que l&#8217;exécution d&#8217;un programme interprété ne semble pas systématiquement ralentie.</p>
<h3>Comment créer un interpréteur&nbsp;?</h3>
<p>J&#8217;ai été amené à créer plusieurs interpréteurs. Un interpréteur Lisp minimal, un interpréteur de script compatible Bash, un évaluateur mathématique assez poussé dans un outil de génération de PDF. Et je confirme&nbsp;: sans être impossible, ce n&#8217;est quand même pas simple.</p>
<p>Et encore, il ne s&#8217;agissait que de simples interpréteurs. Pas de bytecode ni de machine virtuelle, et encore moins de compilation native. J&#8217;ai bien codé une machine virtuelle particulièrement sommaire durant mes études, mais elle émulait un ordinateur tellement simple que ça ne serait d&#8217;aucune utilité.</p>
<p>Par contre, j&#8217;ai suivi des cours de compilation, durant lesquels j&#8217;ai appris à utiliser <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Lex_et_yacc" target="_blank">Lex et Yacc</a>, qui forment un <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Compilateur_de_compilateur" target="_blank">compilateur de compilateur</a>. Ils offrent les outils de base nécessaires à la réalisation d&#8217;un interpréteur ou d&#8217;un compilateur (<a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Analyse_lexicale" target="_blank">analyse lexicale</a>, <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Analyse_syntaxique" target="_blank">analyse syntaxique</a>)&nbsp;: ils permettent de lire un fichier et de le &laquo;&nbsp;comprendre&nbsp;&raquo;&nbsp;; à partir de là, on peut soit exécuter du code (interprétation directe), soit construire un arbre d&#8217;exécution (pour ensuite interpréter ou compiler l&#8217;arbre).</p>
<p>Je dois bien avouer que je n&#8217;ai pas utilisé ces programmes une seule fois de toute ma carrière. Ils ne sont pas particulièrement simples à appréhender, mais rien de fondamentalement impossible.<br />
La grande difficulté n&#8217;est pas spécialement d&#8217;utiliser Lex/Yacc pour extraire les <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Lex%C3%A8me" target="_blank">lexèmes</a> qui composent un code source. La grande difficulté, c&#8217;est de construire un arbre efficace et de le traiter correctement. C&#8217;est la différence entre un petit interpréteur rigolo et un vrai langage.</p>
<h3>La solution&nbsp;?</h3>
<p>J&#8217;en viens enfin à ma recette miracle. Il existe un compilateur C qui présente trois caractéristiques très intéressantes&nbsp;:<span id="more-1034"></span></p>
<ol>
<li>Il est particulièrement rapide. Il applique forcément moins de d&#8217;optimisations sur le code qu&#8217;un compilateur comme GCC, mais n&#8217;en produit pas moins du code binaire natif très efficace.</li>
<li>Il peut compiler et exécuter à la volée. Conjugué au point précédent, cela veut dire qu&#8217;on peut s&#8217;en servir comme d&#8217;un interpréteur C. Mais, contrairement aux quelques rares interpréteurs qui utilisent une syntaxe proche du C, le code n&#8217;est pas interprété&nbsp;; il est compilé nativement − très très vite − puis exécuté, et tout ça sans écrire de fichier sur le disque.</li>
<li>Il est disponible sous la forme d&#8217;une bibliothèque de fonctions, elle-même écrite en C, qui permet d&#8217;exécuter du code à la volée. Cette bibliothèque permet même de partager des symboles&nbsp;; ainsi, le programme qui l&#8217;utilise pour compiler et exécuter du code à la volée pourra accéder directement aux données et aux fonctions de ce code, et inversement&nbsp;!</li>
</ol>
<p>Ce compilateur, c&#8217;est <a title="tinycc.org" href="http://www.tinycc.org" target="_blank">TinyCC</a>. Il est écrit par le français <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Fabrice_Bellard" target="_blank">Fabrice Bellard</a>, qui a aussi à son actif l&#8217;émulateur <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/QEMU" target="_blank">QEMU</a> ou encore la bibliothèque <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/FFmpeg" target="_blank">FFmpeg</a>.</p>
<p>Je suis en train de réaliser divers benchmarks en ce moment, pour comparer plusieurs langages de programmation. Je publierai les résultats sur ce blog quand ce sera terminé. Mais je peux déjà vous dire que, pour un code orienté objet avec de l&#8217;héritage et du polymorphisme, beaucoup d&#8217;instanciations d&#8217;objets, des parcours de tableaux et quelques calculs mathématiques, j&#8217;obtiens pour le moment les résultats suivants&nbsp;:</p>
<ul>
<li>code C compilé avec GCC&nbsp;: 24 ms</li>
<li>code C++ compilé avec G++&nbsp;: 25 ms</li>
<li>code C compilé à la volée avec TinyCC&nbsp;: 150 ms</li>
<li>code PHP&nbsp;: 2934 ms</li>
</ul>
<p>Bon, en soit ça ne veut pas dire grand chose (je publierai les codes sources en même temps que l&#8217;ensemble des résultats du benchmark). Mais on se rend quand même compte que TinyCC est très efficace (pour info, la compilation avec GCC prend environ 60 ms).</p>
<p>L&#8217;idée que j&#8217;ai en tête, c&#8217;est que plutôt que d&#8217;écrire un interpréteur complet (travail de titan), il est certainement plus simple d&#8217;écrire un programme qui traduit une syntaxe donnée en code C, que TinyCC peut compiler et exécuter à la volée. En plus, cela offre la possibilité de compiler le code nativement.</p>
<p>D&#8217;une certaine manière cela veut dire que le code C devient le bytecode, le langage intermédiaire qui est utilisé pour produire le binaire final.<br />
Je sais que ça peu sembler tordu, mais je suis très humble quant à ma capacité à générer du bytecode et à créer une machine virtuelle. Par contre, je pense pouvoir être capable de créer un traducteur qui génère du code C à partir d&#8217;un autre code source.<br />
La vraie question, c&#8217;est de savoir si cette étape de traduction ne risque pas à elle seule de plomber les performances d&#8217;un tel interpréteur&#8230;</p>
<p><span style="color: #800000;">Edit&nbsp;: Je découvre a posteriori que cette idée n&#8217;est pas si nouvelle que ça. Le projet <a href="http://smarteiffel.loria.fr/" target="_blank"><span style="color: #800000;">SmartEiffel</span></a> (un compilateur open-source du langage <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Eiffel_(langage)" target="_blank"><span style="color: #800000;">Eiffel</span></a>) effectue la traduction du code Eiffel en code C (ou en bytecode Java), pour ensuite effectuer une compilation native grâce à GCC.</span></p>
<h3>Faisons un test&nbsp;!</h3>
<p>Allez, soyons fou, essayons d&#8217;écrire un traducteur ultra-simpliste.</p>
<p>Créons un langage de programmation dont la syntaxe est très simpliste. Il n&#8217;accepte que 3 types de commandes. Voici un exemple de code source (bon, ça fait un peu pompeux d&#8217;appeler ça du <em>code source</em>, mais on est là pour s&#8217;amuser)&nbsp;:</p>
<pre><span style="color: #888888;"># affecte une variable</span>
<span style="color: #888888;"># leurs noms commencent par un dollar suivi d'une seule lettre</span>
AFF $a = 3
<span style="color: #888888;"># incrémente une variable</span>
<span style="color: #888888;"># si elle n'avait pas été affectée, elle est initialisée à 0</span>
INC $b
<span style="color: #888888;"># affichage conditionnel</span>
<span style="color: #888888;"># écrit le texte après les deux-points si la condition est vraie</span>
IF $a &lt;= 3&nbsp;: Texte</pre>
<p>Nous allons maintenant écrire un programme qui lit un fichier de code source (passez-moi l&#8217;expression), génère du code C dans une variable, puis utilise la bibliothèque libtcc pour compiler et exécuter ce code.</p>
<p>Le début du programme est facile. Quelques inclusions basiques, la définition des prototypes de deux fonctions utilitaires et la fonction principale du programme. Celle-ci alloue une zone mémoire qui contiendra le code C, puis lance la lecture du fichier source et la génération du code C, ensuite elle affiche le contenu de code C généré, et enfin elle appelle la fonction qui exécute le code C.</p>
<pre>#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#include &lt;libtcc.h&gt;

<span style="color: #888888;">// prototypes</span>
void execute(char*);
void readfile(char*, char*);

<span style="color: #888888;">/* fonction principale */</span>
void main(int argc, char **argv)
{
   char result[1024];

   <span style="color: #888888;">// lecture du fichier et génération du code C</span>
   readfile(argv[1], result);
   <span style="color: #888888;">// affichage du code C généré</span>
   printf("CODE C&nbsp;:\n%s\n", result);
   <span style="color: #888888;">// exécution du code C généré</span>
   execute(result);
}</pre>
<p>La fonction de lecture et de génération de code initialise quelques variables, ouvre le fichier contenant le code source, lit les instructions qui y sont écrites et ajoute l&#8217;équivalent en C dans une chaîne de caractères. Il y a une petite subtilité au sujet des variables&nbsp;: à chaque instruction, on complète un tableau pour indiquer quelles sont les variables qui sont utilisées, afin de faciliter leur déclaration en C.</p>
<p><span style="color: #000080;">Edit&nbsp;: J&#8217;ai modifié légèrement cette fonction pour gérer les lignes de commentaire (commençant par un caractère dièse). Ce sont les deux lignes en bleu. J&#8217;explique plus bas l&#8217;utilité réelle de la chose.</span></p>
<pre><span style="color: #888888;">/* lit le fichier et génère le code C */</span>
void readfile(char *filename, char *result)
{
   FILE *input;
   char line[128], buf[128], msg[128], op1, cond[3];
   char code[1024], variables[128];
   int i;

   <span style="color: #888888;">// initialisations</span>
   result[0] = code[0] = '\0';
   strcat(result, "int wrapper() {\n");
   for (i = 0; i &lt; 128; ++i)
      variables[i] = '\0';
   <span style="color: #888888;">// ouverture du fichier source</span>
   input = fopen(filename, "r");
   <span style="color: #888888;">// lecture de toutes les lignes du fichier</span>
   while (!feof(input) &amp;&amp; fgets(line, 128, input))
   {
      <span style="color: #888888;">// détection de la commande</span>
      <span style="color: #000080;">if (line[0] == '#')</span>
         <span style="color: #000080;">continue;</span>
      if (sscanf(line, "AFF $%c = %d", &amp;op1, &amp;i))
         sprintf(buf, "\t%c = %d;\n", op1, i);
      else if (sscanf(line, "INC $%c", &amp;op1))
         sprintf(buf, "\t%c++;\n", op1);
      else if (sscanf(line, "IF $%c %s %d&nbsp;: %s", &amp;op1, cond,
                      &amp;i, msg))
         sprintf(buf, "\tif (%c %s %d) printf(\"%s\\n\");\n",
                 op1, cond, i, msg);
      variables[op1] = op1;
      strcat(code, buf);
   }
   fclose(input);
   <span style="color: #888888;">// définition des variables</span>
   for (i = 0; i &lt; 128; ++i)
   {
      if (variables[i])
      {
         sprintf(buf, "\tint %c = 0;\n", i);
         strcat(result, buf);
      }
   }
   <span style="color: #888888;">// ajout du code</span>
   strcat(result, code);
   strcat(result, "\treturn 0;\n}\n");
}</pre>
<p>Enfin, la fonction d&#8217;exécution fait compiler le code C par TinyCC, place le binaire exécutable dans une zone mémoire exploitable, puis appelle la fonction que l&#8217;on a créée préalablement.</p>
<pre><span style="color: #888888;">/* exécute le code généré à la volée */</span>
void execute(char *code)
{
   TCCState *state;
   int (*wrapper)(void);
   int size;
   void *memory;

   <span style="color: #888888;">// création du contexte TinyCC</span>
   state = tcc_new();
   tcc_set_output_type(state, TCC_OUTPUT_MEMORY);
   <span style="color: #888888;">// compilation du code C</span>
   tcc_compile_string(state, code);
   <span style="color: #888888;">// réallocation en mémoire</span>
   size = tcc_relocate(state, NULL);
   memory = malloc(size);
   tcc_relocate(state, memory);
   <span style="color: #888888;">// récupération du pointeur sur la fonction</span>
   wrapper = tcc_get_symbol(state, "wrapper");
   <span style="color: #888888;">// libération de la mémoire allouée par TinyCC</span>
   tcc_delete(state);
   <span style="color: #888888;">// exécution de la fonction</span>
   wrapper();
   <span style="color: #888888;">// libération de la mémoire contenant le code binaire</span>
   free(memory);
}</pre>
<p>Pour plus d&#8217;information sur l&#8217;utilisation de libtcc, je ne peux que vous conseiller de lire <a title="Site du Zéro" href="http://www.siteduzero.com/tutoriel-3-268685-compilation-a-la-volee-avec-libtcc.html" target="_blank">un très bon article sur le Site du Zéro</a>.</p>
<h3>Exécution</h3>
<p>Maintenant que nous avons notre super interpréteur, nous allons l&#8217;utiliser.</p>
<p>Imaginons que notre code source ressemble à ceci&nbsp;:</p>
<pre>AFF $a = 3
AFF $b = 1
IF $a &lt; 5&nbsp;: ok
IF $b &gt; 7&nbsp;: ko
AFF $c = 5
INC $c
IF $c &gt;= 6&nbsp;: parfait</pre>
<p>Quand on exécute notre interpréteur, il nous affiche la fonction C générée&nbsp;:</p>
<pre>int wrapper() {
   int a = 0;
   int b = 0;
   int c = 0;
   a = 3;
   b = 1;
   if (a &lt; 5) printf("ok\n");
   if (b &gt; 7) printf("ko\n");
   c = 5;
   c++;
   if (c &gt;= 6) printf("parfait\n");
   return 0;
}</pre>
<p>Et donc, le résultat de l&#8217;exécution est donc&nbsp;:</p>
<pre>ok
parfait</pre>
<p>Super, ça marche&nbsp;! Trop fort.</p>
<p><span style="color: #000080;">Edit&nbsp;: Pour rendre notre code source directement exécutable (c&#8217;est quand même bien utile, quand on fait un interpréteur), il suffit de lui donner les droits d&#8217;exécution avec la commande chmod, et d&#8217;ajouter le <a title="Wikipédia" href="http://fr.wikipedia.org/wiki/Shebang" target="_blank"><span style="color: #000080;">shebang</span></a> qui conduit vers le programme d&#8217;interprétation&nbsp;:</span></p>
<pre><span style="color: #000080;">#!/home/amaury/minilang</span>
AFF $a = 3
AFF $b = 1
IF $a &lt; 5&nbsp;: ok
IF $b &gt; 7&nbsp;: ko
AFF $c = 5
INC $c
IF $c &gt;= 6&nbsp;: parfait</pre>
<p><span style="color: #000080;">Oh miracle&nbsp;! Le programme s&#8217;exécute directement. Même en sachant que ça doit marcher, c&#8217;est plaisant à voir.</span></p>
<p>Par acquis de conscience, je décide de comparer avec le code PHP suivant&nbsp;:</p>
<pre>&lt;?php
   $a = 3;
   $b = 1;
   if ($a &lt; 5)
      print("ok\n");
   if ($b &gt; 7)
      print("ko\n");
   $c = 5;
   $c++;
   if ($c &gt;= 6)
      print("parfait\n");
?&gt;</pre>
<p>Et hop, un autre benchmark inutile mais rigolo&nbsp;:</p>
<ul>
<li>PHP&nbsp;: 25 ms</li>
<li>mon interpréteur&nbsp;: 2 ms</li>
</ul>
<p>Eh oui, c&#8217;est débile, ça ne sert à rien, mais ça arrache et ça me fait marrer. Évidemment, plus on ajoutera de vraies fonctionnalités à notre interpréteur pour enrichir sa syntaxe, plus on le ralentira. Mais je pense que ça prouve quand même que mon idée de base est loin d&#8217;être idiote&nbsp;: Si vous voulez créer un langage de programmation, utilisez le C comme &laquo;&nbsp;langage pivot&nbsp;&raquo;&nbsp;; ce sera plus rapide à mettre en œuvre et plus efficace que si vous développiez votre propre bytecode et votre propre machine virtuelle. En plus vous pourrez accéder à toutes les bibliothèques de fonction disponibles en C, ce qui est loin d&#8217;être négligeable.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2012/01/11/les-langages-de-programmation-partie-3-creer-un-interpreteur-tinycc-inside/feed</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>Les langages de programmation &#8211; Partie 2 : Le modèle objet</title>
		<link>http://www.geek-directeur-technique.com/2012/01/10/les-langages-de-programmation-partie-2-le-modele-objet</link>
		<comments>http://www.geek-directeur-technique.com/2012/01/10/les-langages-de-programmation-partie-2-le-modele-objet#comments</comments>
		<pubDate>Tue, 10 Jan 2012 00:20:52 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Questions techniques]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[classe]]></category>
		<category><![CDATA[héritage]]></category>
		<category><![CDATA[interface]]></category>
		<category><![CDATA[mixin]]></category>
		<category><![CDATA[objet]]></category>
		<category><![CDATA[polymorphisme]]></category>
		<category><![CDATA[POO]]></category>
		<category><![CDATA[programmation]]></category>
		<category><![CDATA[trait]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=984</guid>
		<description><![CDATA[Après vous avoir parlé des langages que je connais (petit moment narcissique inutile), je vais maintenant partager quelques réflexions concernant le modèle objet, et comment il est implémenté dans les langages de programmation. Les objets, l&#8217;héritage et le polymorphisme La notion la plus importante de la programmation orientée objet, c&#8217;est&#8230; l&#8217;objet (tadaa&#160;!), une structure qui [...]]]></description>
			<content:encoded><![CDATA[<p>Après vous avoir parlé des <a title="Les langages de programmation – Partie 1&nbsp;: Ce que je connais" href="http://www.geek-directeur-technique.com/2012/01/06/les-langages-de-programmation-partie-1-ce-que-je-connais">langages que je connais</a> (petit moment narcissique inutile), je vais maintenant partager quelques réflexions concernant le modèle objet, et comment il est implémenté dans les langages de programmation.</p>
<h3>Les objets, l&#8217;héritage et le polymorphisme</h3>
<p>La notion la plus importante de la <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Programmation_orient%C3%A9e_objet" target="_blank">programmation orientée objet</a>, c&#8217;est&#8230; l&#8217;objet (tadaa&nbsp;!), une structure qui encapsule en même temps des données et les traitements qui y sont associés. Très vite, on trouve derrière l&#8217;<a title="Wikipedia" href="http://fr.wikipedia.org/wiki/H%C3%A9ritage_(informatique)" target="_blank">héritage</a> et le <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Polymorphisme_(informatique)" target="_blank">polymorphisme</a>.</p>
<p><strong>L&#8217;héritage</strong>&nbsp;: Un objet peut dériver d&#8217;un autre. Il en reprend toutes les caractéristiques, en y ajoutant des attributs (données) et/ou des méthodes (traitements) supplémentaires. La plupart du temps, il est même possible de suppléer une méthode de l&#8217;objet parent, en redéfinissant une méthode du même nom.</p>
<p>Prenons un exemple. L&#8217;objet <em>Véhicule</em> possède les attributs <em>poids</em> et <em>vitesse</em>, et la méthode <em>avance</em>. Les objets <em>Voiture</em> et <em>Camion</em> héritent de <em>Véhicule</em>, en ajoutant des attributs supplémentaires. Voici un petit diagramme UML&nbsp;:</p>
<p><a href="http://www.geek-directeur-technique.com/wp-content/uploads/2012/01/poo1.png"><img class="alignnone size-full wp-image-991" title="POO - Héritage" src="http://www.geek-directeur-technique.com/wp-content/uploads/2012/01/poo1.png" alt="" width="350" height="239" /></a></p>
<p>Si on crée une instance de l&#8217;objet Voiture, on aura accès aux attributs poids, vitesse et nombre_passager, et aux méthodes avance et allume_autoradio.</p>
<p><strong>Le polymorphisme</strong> : Tout les objets qui dérivent d&#8217;un même objet (avec différent sous-types) peuvent être manipulés comme s&#8217;ils étaient du type de l&#8217;objet parent − tant qu&#8217;on n&#8217;utilise que leur partie commune.</p>
<p>Si on reprend l&#8217;exemple précédent, toutes les instances des objets <em>Voiture</em> et <em>Camion</em> peuvent être manipulées comme si elles étaient du type <em>Véhicule</em>. Si on veut calculer leur poids total, il suffit d&#8217;additionner la somme des poids de tous les véhicules, sans avoir besoin de savoir s&#8217;il s&#8217;agit de voitures ou de camion.</p>
<p>Par contre, seuls les camions peuvent charger une cargaison.</p>
<p>Après l&#8217;héritage et le polymorphisme, on peut ajouter l&#8217;<a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Encapsulation_(programmation)" target="_blank">encapsulation</a> et la notion de visibilité, qui permettent de déterminer les règles d&#8217;accès aux attributs et aux méthodes dans les objets et depuis l&#8217;extérieur des objets. Bien que ces notions soient très répandues, il existe des langages orientés objet qui ne les supportent pas (<a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Javascript" target="_blank">Javascript</a>, <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Python_(langage)" target="_blank">Python</a>).</p>
<h3>Implémentation des types d&#8217;héritages</h3>
<p>Il existe deux types d&#8217;héritage&nbsp;: l&#8217;héritage simple et l&#8217;<a title="Wikipedia" href="http://fr.wikipedia.org/wiki/H%C3%A9ritage_multiple" target="_blank">héritage multiple</a>.</p>
<p>L&#8217;avantage de l&#8217;héritage multiple, c&#8217;est qu&#8217;il permet − comme son nom l&#8217;indique − à un objet d&#8217;hériter de plusieurs objets à la fois. Parmi les langages qui le supportent, je ne connais vraiment que le <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/C%2B%2B" target="_blank">C++</a> et le <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Perl_(langage)" target="_blank">Perl</a>,&nbsp; sans oublier le <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Python_(langage)" target="_blank">Python</a> (que je connais mal)&nbsp;; il en existe d&#8217;autres, qui sont plutôt exotiques (<a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Eiffel_(langage)" target="_blank">Eiffel</a>, <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/OCaml" target="_blank">OCaml</a>, &#8230;).</p>
<p>La plupart des langages ne supportent que l&#8217;héritage simple. Enfin, c&#8217;est l&#8217;impression que j&#8217;ai, je ne peux pas vraiment l&#8217;étayer de manière formelle. Mais si on prend l&#8217;exemple du <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Java_(langage)" target="_blank">Java</a>, du <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/PHP" target="_blank">PHP</a>, du <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Ruby" target="_blank">Ruby</a>&#8230; ça semble être la mode.</p>
<p>Toujours sans la moindre preuve, j&#8217;ai tendance à penser que l&#8217;héritage multiple est mis de côté pour simplifier le développement des langages. Je m&#8217;explique. Dans les années 80, il n&#8217;existait pas de compilateur C++. <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Bjarne_Stroustrup" target="_blank">Bjarne Soustrup</a>, son créateur, avait mis à disposition un programme qui traduisait du code C++ en code C, qui pouvait alors être compilé. J&#8217;ai été amené, durant mes études, à m&#8217;intéresser à ce type de translateur, et c&#8217;est très intéressant.</p>
<p>Ce qu&#8217;il faut comprendre, c&#8217;est qu&#8217;il est possible de mettre en œuvre des techniques de programmation orientée objet en C. Cela est notamment utilisé dans les bibliothèques graphiques comme (<a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Motif_(biblioth%C3%A8que_graphique)" target="_blank">Motif</a>, <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/LessTif" target="_blank">LessTif</a> ou <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/GTK%2B" target="_blank">GTK+</a>), pour gérer leurs éléments&nbsp;: une fenêtre ou un bouton sera géré simplement comme un widget générique par moments.<br />
Je vais expliquer le principe de ce genre de techniques, sachant que cela peut aller bien plus loin que ce que je vais vous montrer.</p>
<p><span id="more-984"></span>Imaginons un code C qui contiennent une structure <em>Véhicule</em>, et la fonction <em>avance</em>&nbsp;:</p>
<pre>typedef struct {
 &nbsp;  int poids;
 &nbsp;  short vitesse;
} vehicule_t;

void avance(vehicule_t *vehicule, short vitesse) {
    vehicule-&gt;vitesse = vitesse;
    printf("Le véhicule avance à %d km/h", vitesse);
}</pre>
<p>La fonction <em>avance</em> prend en paramètre un pointeur sur le type correspondant au véhicule, et un seconde paramètre qui indique la vitesse à laquelle le véhicule se déplace. Elle se contente d&#8217;affecter la vitesse dans le champ prévu à cet effet dans la structure.</p>
<p>Si maintenant nous voulons créer l&#8217;objet Voiture, qui hérite de l&#8217;objet Véhicule, c&#8217;est très simple. La structure Voiture doit contenir la structure Véhicule&nbsp;:</p>
<pre>typedef struct {
 &nbsp;  vehicule_t parent;
 &nbsp;  short nombre_passagers;
} voiture_t;

void allume_autoradio(voiture_t *voiture) {
    printf("Il y a %d mélomanes", voiture-&gt;nombre_passagers);
}</pre>
<p>Pourquoi avoir placé la structure &laquo;&nbsp;parente&nbsp;&raquo; au début de la structure &laquo;&nbsp;fille&nbsp;&raquo;&nbsp;? Tout simplement parce qu&#8217;ainsi, nous pouvons retrouver l&#8217;un ou l&#8217;autre à la même adresse en mémoire. En faisant un <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Conversion_de_type" target="_blank">transtypage</a>, on peut facilement se retrouver à manipuler l&#8217;un aussi bien que l&#8217;autre.<br />
Voici un exemple de code, dans lequel on alloue la mémoire pour créer une voiture, mais on va ensuite la faire avancer en l&#8217;utilisant comme n&#8217;importe quel véhicule&nbsp;:</p>
<pre><span style="color: #888888;">// on alloue la structure en mémoire, en l'initialisant à zéro</span>
voiture_t *titine = memset(malloc(sizeof(voiture_t)),
                           0, sizeof(voiture_t));
<span style="color: #888888;">// on allume l'auto-radio de la voiture</span>
allume_autoradio(titine);
<span style="color: #888888;">// on fait avancer la voiture comme n'importe quel véhicule</span>
avance((vehicule_t*)titine, 60);</pre>
<p>Voilà. C&#8217;est super simple, hein&nbsp;? La seule chose, c&#8217;est qu&#8217;il faut faire attention à bien faire la conversion de type à chaque fois qu&#8217;on appelle une fonction qui attend un véhicule, ou lorsqu&#8217;on veut utiliser les champs qui sont dans la structure du véhicule. Mais quand on programme en C, cela semble naturel.</p>
<p>Par exemple, pour récupérer la vitesse de notre voiture, il faut faire&nbsp;:</p>
<pre>((vehicule_t*)titine)-&gt;vitesse</pre>
<p>Et notre objet Camion, qu&#8217;est-ce qu&#8217;il devient&nbsp;?</p>
<pre>typedef struct {
    vehicule_t parent;
 &nbsp;  void* cargaison;
} camion_t;

void charge_cargaison(camion_t *camion, void *cargaison) {
 &nbsp;  camion-&gt;cargaison = cargaison;
}

<span style="color: #888888;">// on alloue la structure en mémoire, en l'initialisant à zéro</span>
camion_t *mack = memset(malloc(sizeof(camion_t)),
                        0, sizeof(camion_t));
<span style="color: #888888;">// on charge une cargaison dans le camion</span>
charge_cargaison(mack, "Flash McQueen");
<span style="color: #888888;">// on fait avancer le camion comme n'importe quel véhicule</span>
avance((vehicule_t*)mack, 60);</pre>
<p>Pas de problème, on voit bien que les camions et les voitures peuvent être manipulés comme des véhicules.</p>
<h3>Surcharge de méthodes</h3>
<p>Imaginons maintenant que l&#8217;on souhaite pouvoir faire de la surcharge de méthodes. Par exemple, on veut laisser la possibilité aux voitures et aux camions de redéfinir le code qui fait avancer le véhicule. Comment faire&nbsp;?</p>
<p>En C, la réponse passe par les pointeurs de fonction. Le code devient plus complexe, mais rien qui ne devrait vous effrayer.</p>
<p>Pour le véhicule, on aurait quelque chose comme ça&nbsp;:</p>
<pre><span style="color: #888888;">// définition de la structure</span>
typedef struct vehicule_s {
 &nbsp;  int poids;
 &nbsp;  short vitesse;
 &nbsp;  void (*avance)(struct vehicule_s*, short);
} vehicule_t;

<span style="color: #888888;">// fonction avance()</span>
void vehicule_avance(vehicule_t *vehicule, short vitesse) {
 &nbsp;  vehicule-&gt;vitesse = vitesse;
    printf("Le véhicule avance à %d km/h", vitesse);
}
<span style="color: #888888;">// fonction servant de "constructeur"</span>
vehicule_t* vehicule_create() {
 &nbsp;  <span style="color: #888888;">// Allocation mémoire</span>
 &nbsp;  vehicule_t *vroum = memset(malloc(sizeof(vehicule_t)),
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  0, sizeof(vehicule_t));
 &nbsp;  <span style="color: #888888;">// initialisation du pointeur de fonction</span>
 &nbsp;  vehicule-&gt;avance = vehicule_avance;
    <span style="color: #888888;">// retour</span>
    return (vroum);
}</pre>
<p>Si on veut créer un véhicule et le faire avancer, c&#8217;est simple&nbsp;:</p>
<pre>vehicule_t *vehicule = vehicule_create();
vehicule-&gt;avance(vehicule, 40);</pre>
<p>Maintenant, faisons un camion&nbsp;; lorsqu&#8217;on lui dit d&#8217;avancer, il va vérifier qu&#8217;on ne lui demande pas d&#8217;aller trop vite&nbsp;:</p>
<pre><span style="color: #888888;">// définition de la structure</span>
typedef struct {
 &nbsp;  vehicule_t parent;
 &nbsp;  void *cargaison;
} camion_t;

<span style="color: #888888;">// fonction de chargement de la cargaison</span>
void charge_cargaison(camion_t *camion, void *cargaison) {
 &nbsp;  camion-&gt;cargaison = cargaison;
}
<span style="color: #888888;">// fonction avance() spécifique aux camions</span>
void camion_avance(vehicule_t *vehicule, short vitesse) {
 &nbsp;  if (vitesse &gt; 90)
 &nbsp; &nbsp; &nbsp;  printf("Ce camion ne dépasse pas 90 km/h");
 &nbsp;  else if (vitesse &gt; 80 &amp;&amp;
             ((camion_t)*vehicule)-&gt;cargaison&nbsp;!= NULL)
 &nbsp; &nbsp; &nbsp;  printf("Ce camion chargé ne dépasse pas 80 km/h");
 &nbsp;  else {
 &nbsp; &nbsp; &nbsp;  vehicule-&gt;vitesse = vitesse;
 &nbsp; &nbsp; &nbsp;  printf("Le camion avance à %d km/h", vitesse);
 &nbsp;  }
}
<span style="color: #888888;">// fonction servant de "constructeur"</span>
camion_t *camion_create() {
 &nbsp;  camion_t *camion = memset(malloc(sizeof(camion_t)),
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0, sizeof(camion_t));
 &nbsp;  ((vehicule_t)camion)-&gt;avance = camion_avance;
    return (camion);
}</pre>
<p>Maintenant, créons un camion, chargeons-lui une cargaison et faisons-le avancer&nbsp;:</p>
<pre>camion_t *mack = camion_create();
charge_cargaison(mack, "Flash McQueen");
((vehicule_t*)mack)-&gt;avance((vehicule_t*)mack, 85);</pre>
<p>Si vous avez bien suivi, ce code affichera le résultat suivant&nbsp;:</p>
<pre>Ce camion chargé ne dépasse pas 80 km/h</pre>
<p>Bref, tout cela fonctionne très bien. Je ne dis pas que c&#8217;est facile à lire, ni que la syntaxe est super évidente. Mais c&#8217;est efficace.</p>
<h3>Pour aller plus loin</h3>
<p>Si la programmation orientée objet en C vous intéresse, n&#8217;hésitez pas à me contacter. À une époque j&#8217;étais allé assez loin dans le concept, avec notamment un support des exceptions, et des macros qui simplifiaient énormément de choses.</p>
<p>Je peux aussi vous recommander les liens suivants&nbsp;:</p>
<ul>
<li><a href="http://www.bolthole.com/OO-C-programming.html" target="_blank">http://www.bolthole.com/OO-C-programming.html</a></li>
<li><a href="http://www.planetpdf.com/codecuts/pdfs/ooc.pdf" target="_blank">http://www.planetpdf.com/codecuts/pdfs/ooc.pdf</a> (PDF de 221 pages)</li>
</ul>
<h3>C&#8217;est bien beau, et alors&nbsp;?</h3>
<p>Qu&#8217;est-ce qu&#8217;on peut retirer de tout ça&nbsp;? Eh bien, l&#8217;héritage simple et le polymorphisme sont des notions franchement pas compliquées à implémenter dans un langage de bas niveau. Je pense que cela fait partie des raisons qui ont conduit à privilégier l&#8217;héritage simple au détriment de l&#8217;héritage multiple. On m&#8217;objectera qu&#8217;il s&#8217;agit d&#8217;un choix philosophique, que les concepteurs de langages ne veulent pas proposer l&#8217;héritage multiple&nbsp;; mais j&#8217;en douterais toujours.</p>
<p>Ce qui m&#8217;énerve un peu, c&#8217;est que s&#8217;est rapidement rendu compte que l&#8217;héritage simple réduit tellement les possibilités de programmation, que des solutions de contournement ont été mises en place.</p>
<p>Ainsi sont arrivées les <a title="Wikpedia" href="http://en.wikipedia.org/wiki/Interface_(object-oriented_programming)" target="_blank">interfaces</a>, qui servent à enrichir la définition d&#8217;un objet (et de partager ces définitions entre plusieurs objets) sans fournir d&#8217;implémentation.<br />
Puis sont arrivés les <a title="Wikipedia" href="http://en.wikipedia.org/wiki/Mixin" target="_blank">mixins</a> et les <a title="Wikipedia" href="http://en.wikipedia.org/wiki/Trait_(computer_programming)" target="_blank">traits</a>, qui fournissent pour leur part des implémentations qui peuvent être intégrées dans des objets.</p>
<p>N&#8217;importe quel développeur C++ vous dirait que ces notions sont inutiles à partir du moment où vous savez créer des <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Classe_abstraite" target="_blank">classes abstraites</a>.</p>
<p>Récemment, je donnais à ce propos deux exemples à un ami.</p>
<ol>
<li>En Pascal, il y a une différence explicite entre les fonctions et les procédures. La différence, c&#8217;est simplement que les procédures ne retournent rien. En C et dans les langages qui en ont découlé, on n&#8217;a toujours que des fonctions, sauf que parfois elles ne retournent rien. C&#8217;est simple à comprendre, pas besoin de se retourner le cerveau.</li>
<li>Autre parallèle&nbsp;: Que je veuille ouvrir mon ordinateur ou démonter un meuble, j&#8217;utilise le même tournevis cruciforme. J&#8217;ai beau aimer les meubles Ikea (ils sont faciles et rapides à monter), ça m&#8217;énerve d&#8217;avoir à aller chercher un clé Allen pour les démonter. Le tournevis cruciforme, c&#8217;est l&#8217;héritage multiple, qui s&#8217;adapte à tous les usages. Les meubles Ikea, c&#8217;est le Java ou le Ruby&nbsp;; ça peut accélérer le développement, mais il n&#8217;y a − à mon sens − aucune bonne raison d&#8217;imposer les clés Allen (les interfaces).</li>
</ol>
<p>Pour ma part, c&#8217;est le cheminement que je trouve bancal&nbsp;: On essaye de justifier la simplification (<em>l&#8217;héritage multiple, c&#8217;est mal</em>), mais on se retrouve à refaire du pseudo-héritage multiple incomplet.<br />
Mais parce que le nom est différent, ça devient acceptable&nbsp;? <em>Mixin</em>, c&#8217;est plus hype&nbsp;?</p>
<p>Il existe des techniques pour gérer le problème de l&#8217;<a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Probl%C3%A8me_du_diamant" target="_blank">héritage en diamant</a> (la <a title="Wikipedia" href="http://en.wikipedia.org/wiki/Depth-first_search" target="_blank">recherche profonde</a> et la <a title="Wikipedia" href="http://en.wikipedia.org/wiki/C3_linearization" target="_blank">linéarisation C3</a>), plusieurs langages les utilisent déjà, il n&#8217;y a donc pas de raison. En plus de ça, si vous avez un objet qui essaye d&#8217;implémenter deux interfaces qui possèdent une méthode dont le nom est identique, ça ne marchera pas mieux pour autant. Dans tous les cas, c&#8217;est au développeur de faire correctement sa modélisation.</p>
<p>Dans le prochain article, je vais tenter de rassembler mes idées sur ce que j&#8217;aimerais avoir comme langage. C&#8217;est pas gagné.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2012/01/10/les-langages-de-programmation-partie-2-le-modele-objet/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Les langages de programmation &#8211; Partie 1 : Ce que je connais</title>
		<link>http://www.geek-directeur-technique.com/2012/01/06/les-langages-de-programmation-partie-1-ce-que-je-connais</link>
		<comments>http://www.geek-directeur-technique.com/2012/01/06/les-langages-de-programmation-partie-1-ce-que-je-connais#comments</comments>
		<pubDate>Fri, 06 Jan 2012 17:45:22 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Questions techniques]]></category>
		<category><![CDATA[basic]]></category>
		<category><![CDATA[C]]></category>
		<category><![CDATA[Fortran]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[Lisp]]></category>
		<category><![CDATA[OCaml]]></category>
		<category><![CDATA[Pascal]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[programmation]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=974</guid>
		<description><![CDATA[Régulièrement (enfin, disons tous les 3/4 ans) je me pose des questions existentielles au sujet des langages de programmation. Pourquoi est-ce que j&#8217;aime tel langage, pourquoi je déteste tel autre, qu&#8217;est-ce que je pourrais vouloir et que je n&#8217;ai pas, et ainsi de suite&#8230; Ne me demandez pas pourquoi, mais ça me reprend en ce [...]]]></description>
			<content:encoded><![CDATA[<p>Régulièrement (enfin, disons tous les 3/4 ans) je me pose des questions existentielles au sujet des langages de programmation. Pourquoi est-ce que j&#8217;aime tel langage, pourquoi je déteste tel autre, qu&#8217;est-ce que je pourrais vouloir et que je n&#8217;ai pas, et ainsi de suite&#8230;</p>
<p>Ne me demandez pas pourquoi, mais ça me reprend en ce moment. Ça va sûrement repartir aussi vite que c&#8217;est venu, mais ça amène quand même quelques réflexions.</p>
<p>Je pense écrire 3 articles sur le sujet. Dans celui-ci, je vais lister les langages que je connais, histoire de situer un peu les choses. Dans le suivant, je parlerai un peu du modèle objet sous un angle inhabituel. Pour terminer, je &laquo;&nbsp;réfléchirai à haute voix&nbsp;&raquo; sur ce que j&#8217;attends d&#8217;un langage de programmation, et pourquoi je serai toujours un éternel insatisfait.</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/BASIC" target="_blank">BASIC</a></h3>
<p>C&#8217;est le premier langage que j&#8217;ai appris. Quand j&#8217;étais gamin, dans les années 80, il y avait deux émissions télévisées au Québec − <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Octo-puce" target="_blank">Octo-puce</a> et <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Octo-giciel" target="_blank">Octo-giciel</a> − qui enseignaient entre autre à programmer en Basic. C&#8217;est comme ça que j&#8217;ai commencé à programmer sur papier en rêvant au jour où j&#8217;aurais un ordinateur. Environ 10 ans plus tard, j&#8217;ai passé un couple d&#8217;années à programmer en <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Visual_Basic" target="_blank">Visual Basic</a> ; c&#8217;était avant de commencer mes études d&#8217;informatique, et je <em>croyais</em> savoir programmer&#8230;</p>
<p>Ce que j&#8217;en retiens, c&#8217;est que le Basic, le &laquo;&nbsp;vrai&nbsp;&raquo; (celui des <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Apple_II" target="_blank">Apple II</a> et <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Commodore_64" target="_blank">Commodore 64</a>) était en fait complètement imbitable. Après coup, j&#8217;ai compris en partie pourquoi <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Edsger_Dijkstra" target="_blank">Dijkstra</a> avait <a title="Wikiquote" href="http://en.wikiquote.org/wiki/Edsger_W._Dijkstra" target="_blank">la dent si dure</a> à son encontre.<br />
Malgré tout, ses déclinaisons successives, depuis le <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Quick_BASIC" target="_blank">QuickBasic</a>, les <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/STOS_BASIC" target="_blank">STOS</a> et <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/AMOS_BASIC" target="_blank">AMOS</a>, jusqu&#8217;au <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/REALbasic" target="_blank">RealBasic</a> et le <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Visual_Basic_.NET" target="_blank">VB.NET</a>, en ont fait un langage moderne et utilisable.</p>
<p>Moderne et utilisable, mais je n&#8217;ai désormais strictement aucune envie de coder en Basic.</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Javascript" target="_blank">Javascript</a></h3>
<p>Je me suis intéressé au Web dès 1995. Je me suis mis alors au HTML, et même à cette époque cela incitait à rapidement se pencher sur le Javascript. Les premières années, je n&#8217;en ai fait qu&#8217;un survol, mais j&#8217;ai fini par mettre en œuvre des techniques assez poussées (notamment concernant la programmation orientée objet en Javascript) qui me sont encore utiles aujourd&#8217;hui au quotidien. Je suis même passé par une phase de développement assez intensive à base de <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Xul" target="_blank">XUL</a>, où tout le code est réalisé en Javascript.</p>
<p>À une époque, j&#8217;étais assez fan du Javascript. J&#8217;aimais sa simplicité et sa malléabilité. J&#8217;ai imaginé l&#8217;utiliser pour des usages très divers. Pour les développements &laquo;&nbsp;client lourd&nbsp;&raquo;, après avoir développé en XUL, j&#8217;en suis un peu revenu&nbsp;; comme expliqué dans <a title="Idée d’entreprise&nbsp;: Plate-forme logicielle" href="http://www.geek-directeur-technique.com/2011/05/04/idee-dentreprise-plate-forme-logicielle">un précédent article</a>, je me suis fatigué de devoir taper 8 lignes de code illisible pour simplement lire le contenu d&#8217;un fichier. Pareil pour les développements côté serveur&nbsp;; j&#8217;ai longtemps pensé que le Javascript combinait assez de qualités pour s&#8217;y montrer à son avantage&nbsp;; cette idée va et vient, depuis le serveur de Netscape dans les années 90, jusqu&#8217;à&nbsp; des projets comme <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/CommonJS" target="_blank">CommonJS</a> et <a title="Wikipedia" href="http://en.wikipedia.org/wiki/Node.js" target="_blank">Node.js</a>.<br />
J&#8217;espère que <a href="http://www.mathieurobin.com/" target="_blank">Mathieu</a> ne m&#8217;en voudra pas, mais l&#8217;ensemble du langage me paraît maintenant trop bancal pour sortir de l&#8217;usage Web client qui est sa spécialité. Entre le modèle objet incomplet, la bibliothèque standard plutôt limitée et une évolution très lente (qui ne laisse pas apercevoir d&#8217;améliorations profondes à courte échéance), je préférerai toujours d&#8217;autres solutions.</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/C_(langage)" target="_blank">C</a></h3>
<p>Ce fut la grande révélation de ma vie. Ce que j&#8217;apprécie dans ce langage, c&#8217;est sa simplicité et sa limpidité. Quelques types scalaires, des structures (et des énumérations et des unions), des fonctions&#8230; et des pointeurs au milieu de tout ça. Difficile de faire plus évident. J&#8217;ai acquis une certaine expertise en C&nbsp;; durant mes études j&#8217;ai fait du développement kernel (Linux et BSD), et par la suite j&#8217;ai travaillé dans le développement système et réseau. C&#8217;est formateur.</p>
<p>On entend souvent dire que le C n&#8217;est qu&#8217;un assembleur à peine amélioré&#8230; C&#8217;est tellement faux&nbsp;! Par contre, ce qui est certain, c&#8217;est qu&#8217;il est quasi-impossible de faire des exécutables plus rapides et plus petits que ceux écrits en C. Bon, il n&#8217;est évidemment pas exempt de défauts, mais qui sont inhérents à ce qu&#8217;il est intrinsèquement. Si vous savez ce que vous faites, le C est un outil redoutable&nbsp;; par contre, si vous vous permettez des approximations, vous ne vous en relèverez pas.</p>
<p><span id="more-974"></span>Aujourd&#8217;hui, je ne code plus du tout en C, au profit du PHP (j&#8217;en parle juste après). Cela m&#8217;ennuie un peu, le manque de pratique émousse le niveau d&#8217;expertise que j&#8217;avais&nbsp;; mais d&#8217;un autre côté, si on peut coder plus vite sans avoir besoin des performances maximales&#8230;</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Perl_(langage)" target="_blank">Perl</a></h3>
<p>J&#8217;ai appris les bases du Perl durant mes études d&#8217;informatique. Comme disait mon professeur d&#8217;administration système (Marc Espie, l&#8217;un des principaux contributeurs à <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/OpenBSD" target="_blank">OpenBSD</a>), “il faut savoir laisser son compilateur au placard quand on a juste besoin d&#8217;un petit script”. Plus tard, j&#8217;ai travaillé pendant 4 ans dans une entreprise où le Perl était le principal langage de développement&nbsp;; j&#8217;y ai considérablement amélioré ma connaissance du langage.</p>
<p>J&#8217;ai apprécié ce langage, qui est particulièrement souple et puissant. Il intègre nativement les listes, les tableaux associatifs et les expressions régulières, ce qui est très pratique au quotidien. Le CPAN est un fantastique outil centralisé pour trouver des bibliothèques, tellement complet qu&#8217;il est difficile à mettre en défaut.</p>
<p>Par contre, j&#8217;ai aussi été profondément gêné par les bizarreries du langage. La syntaxe qui autorise d&#8217;écrire certaines choses &laquo;&nbsp;à l&#8217;envers&nbsp;&raquo;. La notion de référence qui casse les noix quand on manipule des types scalaires (c&#8217;est comme des pointeurs&#8230; mais ça n&#8217;a rien à voir). Les variables magiques (on aime ou on déteste, moi je ne suis pas fan). Le modèle objet est incomplet bien que largement utilisable. Mais surtout, c&#8217;est un langage qui incite <em>presque</em> à faire du code illisible, tellement il permet d&#8217;écrire les choses de plusieurs manières différentes&nbsp;; si vous n&#8217;êtes pas vigilant, vous finissez par privilégier les écritures les plus denses mais les moins maintenables.</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/PHP" target="_blank">PHP</a></h3>
<p>J&#8217;ai commencé à utiliser le PHP dans sa version 3 en 1999. Pendant plusieurs années j&#8217;alternais mes développement serveur entre le PHP et le C. J&#8217;ai fini par m&#8217;habituer aux qualités du PHP, sa simplicité et sa bibliothèque très fournie. Sa syntaxe très inspirée de celles du C et du Perl me convenait très bien, et j&#8217;ai fini par me rendre compte que recompiler un exécutable à chaque modification d&#8217;un site web, c&#8217;est quand même pénible.</p>
<p>J&#8217;ai suivi les évolutions successives du langage, à travers ses versions majeures (4, 5, 5.3) et les différentes améliorations qu&#8217;elles ont apportées. J&#8217;apprécie le PHP pour sa souplesse et son équilibre entre contrainte et permissivité. Syntaxiquement, son intégration des tableaux associatifs est particulièrement réussie, et son modèle objet est complet depuis 2004 (version 5).</p>
<p>La quasi-totalité de mes développements se font désormais en PHP. J&#8217;ai réalisé quelques projets assez pointus avec ce langage (<a title="FineFS, système de fichiers redondé" href="http://www.geek-directeur-technique.com/2009/08/09/finefs-systeme-de-fichiers-redonde">FineFS</a>, <a title="Présentation du framework Temma" href="http://www.geek-directeur-technique.com/2011/07/01/presentation-du-framework-temma">Temma</a>), et je suis très heureux de profiter des facilités qu&#8217;il offre.</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/C%2B%2B" target="_blank">C++</a></h3>
<p>J&#8217;ai fait du C++ à l&#8217;école, je l&#8217;ai même utilisé pour développer un projet sous licence libre (<a href="http://www.headerbrowser.org/" target="_blank">Headerbrowser</a>). C&#8217;est un langage très puissant, qui comble la plupart des lacunes du C, tout en gardant une syntaxe très similaire (au contraire de l&#8217;ObjectiveC, par exemple).</p>
<p>Malgré tout, je me suis toujours senti &laquo;&nbsp;le cul entre deux chaises&nbsp;&raquo; avec le C++. Si j&#8217;ai un grand besoin de performance ou de portabilité, j&#8217;aurai tendance à choisir le C pour sa simplicité. Si je cherche un langage de haut niveau, je prendrais le PHP pour son typage faible et sa gestion dynamique de la mémoire. Certaines fonctionnalités du C++ complexifient la syntaxe à l&#8217;extrême. Nul doute que cela ne soit qu&#8217;une question d&#8217;habitude, mais je n&#8217;arrive pas à m&#8217;y faire.</p>
<p>J&#8217;ai parfois hésité à écrire du code C++ comme si je codais en C, en utilisant seulement les fonctionnalités qui m&#8217;intéressaient (les objets et les exceptions, par exemple). Mais j&#8217;ai lu tellement de choses disant que c&#8217;est une mauvaise idée&#8230; Je vais peut-être revenir là-dessus, parce que les donneurs de leçon qui disent que tel ou tel truc, &laquo;&nbsp;il faut le faire à fond ou pas du tout&nbsp;&raquo;, j&#8217;ai fini par ne plus les écouter.</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Java_(langage)" target="_blank">Java</a></h3>
<p>J&#8217;ai appris le Java durant mes études d&#8217;informatique. Pour quelqu&#8217;un qui s&#8217;orientait vers le développement système, ce langage ne jouissait pas d&#8217;une bonne réputation.<br />
Je l&#8217;ai au final très peu utilisé en dehors du cadre académique. Dans toute ma carrière, je n&#8217;ai qu&#8217;une expérience d&#8217;un an en <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Java_EE" target="_blank">JEE</a>.</p>
<p>J&#8217;ai trouvé que le Java était un plutôt bon langage. Sa grande force vient de sa bibliothèque standard d&#8217;objets, qui est très complète et très proprement structurée. J&#8217;ai par contre connu des problèmes avec l&#8217;environnement général, pas évident à mettre en place au premier abord.</p>
<p>Dans le cadre du développement Web, j&#8217;ai trouvé le JEE d&#8217;une lourdeur incroyable. L&#8217;ensemble des technologies impliquées est vaste et complexe. À mon sens, inutilement vaste et complexe. Beaucoup de choses semblent séduisantes d&#8217;un point de vue théorique, mais leur utilisation est malaisée, ce qui allonge les temps de développement et les coûts associés.</p>
<p>Au final, j&#8217;ai un peu les mêmes griefs que pour le C++. Pour un langage qui se veut de haut niveau, il lui manque beaucoup de facilités d&#8217;écriture, tout en manquant de clarté sur plein de points. S&#8217;il fallait faire un match en C++ et Java, je les mettrais ex æquo. Le Java me semble plus clair, et son API standard est ultra-complète&nbsp;; par contre, le C++ est plus dans mes habitudes par son absence de machine virtuelle et sa compatibilité avec le C (aussi bien le code que les bibliothèques compilées).</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Fortran" target="_blank">Fortran</a></h3>
<p>J&#8217;en parle juste pour déconner. J&#8217;ai fait un peu de Fortran quand j&#8217;étais en fac de biologie. Je n&#8217;en garde presque aucun souvenir. Il n&#8217;empêche que ce langage a continué à évoluer, qu&#8217;il est toujours beaucoup utilisé dans la communauté scientifique, et que c&#8217;est l&#8217;un des langages les plus facilement optimisable sur des architectures réparties (il n&#8217;y avait pas d&#8217;allocation dynamique de la mémoire jusqu&#8217;au Fortran90).</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Pascal_(langage)" target="_blank">Pascal</a></h3>
<p>Là aussi, j&#8217;en parle juste pour le fun. Quand j&#8217;étais petit et que je commençais à m&#8217;intéresser à l&#8217;informatique, un ami informaticien qui avait fait le <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Massachusetts_Institute_of_Technology" target="_blank">MIT</a> m&#8217;a donné un livre traitant de la programmation en Pascal. Je n&#8217;y ai pas compris grand chose sur le moment. Bien plus tard, alors étudiant en informatique, je me souviens avoir aidé des amis qui étaient en prépa, et qui bûchaient sur de la programmation en Pascal. J&#8217;ai pu comprendre sans problème les bases du langage en quelques secondes. Et j&#8217;ai compris de quoi mon prof de C parlait, lorsqu&#8217;il évoquait les différences entre les langages créés par les mathématiciens suisses et ceux créés par les hippies américains. Niveau syntaxe et clarté, pas de soucis, je préfère les baba-cools.</p>
<h3><a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Lisp" target="_blank">Lisp</a> et <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/OCaml" target="_blank">OCaml</a></h3>
<p>Oui, je continue avec les trucs à la con. Juste pour la petite histoire, j&#8217;ai eu à développer un interpréteur de Lisp durant mes études. La syntaxe est tellement simple que ce type d&#8217;interpréteur n&#8217;est vraiment pas très compliqué à coder. Bon, de là à vouloir coder en Lisp au quotidien, c&#8217;est une autre paire de manches. À une époque, c&#8217;était une idée tout à fait valable quand on voulait intégrer un langage dans un logiciel, pour permettre d&#8217;écrire des extensions (comme dans <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Emacs" target="_blank">Emacs</a>, par exemple).</p>
<p>L&#8217;OCaml, je l&#8217;ai étudié durant mes études. Avec le Lisp et l&#8217;OCaml, je me suis vite rendu compte que j&#8217;avais beaucoup de mal à me faire aux langage fonctionnels (je sais, OCaml peut être considéré comme à la fois fonctionnel et impératif, mais zut). J&#8217;ai sûrement été trop marqué par les langages procéduraux, mais on peut quand même remarquer que les langages fonctionnels sont globalement l&#8217;apanage des chercheurs et des mathématiciens, alors que les langages procéduraux sont utilisés par &laquo;&nbsp;ceux qui veulent faire du vrai code&nbsp;&raquo;&nbsp;; je forcis le trait, mais il y a un peu de vrai là-dedans.</p>
<p>Et pourtant, on peut voir l&#8217;héritage des langages fonctionnels se cacher dans le Javascript, ou dans des notions comme le <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/MapReduce" target="_blank">map-reduce</a>. Il est important de connaître le principe, même sans l&#8217;utiliser au quotidien.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2012/01/06/les-langages-de-programmation-partie-1-ce-que-je-connais/feed</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>Configuration Apache en SSL avec wildcard</title>
		<link>http://www.geek-directeur-technique.com/2011/12/29/configuration-apache-en-ssl-avec-wildcard</link>
		<comments>http://www.geek-directeur-technique.com/2011/12/29/configuration-apache-en-ssl-avec-wildcard#comments</comments>
		<pubDate>Thu, 29 Dec 2011 12:24:54 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Questions techniques]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[certificat]]></category>
		<category><![CDATA[ssl]]></category>
		<category><![CDATA[StartSSL]]></category>
		<category><![CDATA[tls]]></category>
		<category><![CDATA[wildcard]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=962</guid>
		<description><![CDATA[Je suis en train de travailler sur un projet web pour lequel j&#8217;ai voulu que toutes les connexions soient sécurisée par défaut en SSL. Avec une subtilité&#160;: Il faut pouvoir gérer des sous-domaines multiples, que l&#8217;on ne connait pas par avance. Je vais vous expliquer point par point comment j&#8217;ai procédé, depuis la génération des [...]]]></description>
			<content:encoded><![CDATA[<p>Je suis en train de travailler sur un projet web pour lequel j&#8217;ai voulu que toutes les connexions soient sécurisée par défaut en SSL. Avec une subtilité&nbsp;: Il faut pouvoir gérer des sous-domaines multiples, que l&#8217;on ne connait pas par avance.</p>
<p>Je vais vous expliquer point par point comment j&#8217;ai procédé, depuis la génération des certificats &laquo;&nbsp;wildcard&nbsp;&raquo; jusqu&#8217;à la configuration Apache.</p>
<h3>Pré-requis et informations préalables</h3>
<p>Considérons que nous avons déjà un serveur Linux avec Apache qui tourne dessus. Dans mon cas, j&#8217;ai travaillé sur une distribution Ubuntu 11.04, mais j&#8217;imagine que les mêmes étapes s&#8217;appliqueront à l&#8217;identique sur Fedora, Suse, ou autres.</p>
<p>Les certificats SSL représentent un business important. Des entreprises se font beaucoup d&#8217;argent en vendant très cher une prestation technique qui est automatisée. Chaque option supplémentaire est facturée à prix d&#8217;or. Suivant l&#8217;entreprise qui fournit le certificat, les tarifs peuvent aller de 12$ à 150$ pour sécuriser un unique nom de domaine, et les certificats wildcard − qui permettent de sécuriser tous les sous-domaines d&#8217;un domaine donné − peuvent atteindre les 500$.<br />
Et encore, si vous allez vers un grand nom comme VeriSign, vous dépassez allègrement le millier de dollars. Par an&nbsp;!</p>
<p>J&#8217;ai donc choisi de me tourner vers <a href="http://www.startssl.com/" target="_blank">StartSSL</a>. Cette autorité de certification fournit gratuitement des certificats de classe 1 (sans vérification d&#8217;identité). Pour les certificats de classe 2 − intégrant les wildcard − ils facturent uniquement l&#8217;opération humaine de vérification d&#8217;identité. On peut ensuite générer autant de certificats que nécessaire, <strong>c&#8217;est gratuit</strong>&nbsp;; il suffit d&#8217;abord de prouver que l&#8217;on est propriétaire des domaines ciblés (la procédure est automatisée, je vais revenir dessus).<br />
Ce fonctionnement est au final complètement logique. L&#8217;ensemble de la chaîne est automatisé et ne génère aucun coût pour StartSSL, à part la vérification d&#8217;identité qui est manuelle. Cette opération est facturée 60$ (47,80 € au cours d&#8217;hier) et est valable pendant deux ans.</p>
<h3>La génération de certificat</h3>
<p>Je me suis largement inspiré d&#8217;une <a href="http://howto.biapy.com/fr/debian-gnu-linux/serveurs/http/creer-un-certificat-ssl-sur-debian" target="_blank">documentation trouvée sur le site Biapy</a>. La procédure est au finale assez simple.</p>
<p>Si OpenSSL n&#8217;est pas installé, c&#8217;est le moment de le faire&nbsp;:</p>
<pre>$&gt; apt-get install openssl</pre>
<p>Idem pour l&#8217;activation du module SSL d&#8217;Apache&nbsp;:</p>
<pre>$&gt; a2enmod ssl</pre>
<p>Imaginons que l&#8217;on possède le nom de domaine <strong>xyz.com</strong>, et que l&#8217;on souhaite en sécuriser tous les sous-domaine grâce à un certificat wildcard <strong>*.xyz.com</strong>.</p>
<p>Commençons par créer la clé privée&nbsp;:</p>
<pre>$&gt; openssl genrsa -out /etc/ssl/private/xyz.com.key 2048
$&gt; chmod 400 /etc/ssl/private/xyz.com.key</pre>
<p>On va ensuite créer le CSR (certificate signing request), qui sera transmis par la suite à StartSSL pour récupérer la clé publique&nbsp;:</p>
<pre>$&gt; mkdir /etc/ssl/reqs
$&gt; openssl req -new -key /etc/ssl/private/xyz.com.key \
   -out /etc/ssl/reqs/xyz.com.csr</pre>
<p><span id="more-962"></span>Le programme va demander plusieurs informations&nbsp;:</p>
<ul>
<li>Notre pays, sur deux lettres (“FR” dans mon cas).</li>
<li>Le nom de notre province, état ou région (“Ile-de-France” pour moi).</li>
<li>Le nom de notre ville (“Paris” si vous êtes parisien).</li>
<li>Le nom de notre entreprise ou de votre organisation.</li>
<li>Le nom de notre unité organisationnelle (moi je laisse vide).</li>
<li>Le “Common Name”, c&#8217;est-à-dire le nom de domaine à sécuriser (“<strong>*</strong>.xyz.com”, attention à bien mettre l&#8217;étoile au début quand on crée un wildcard).</li>
<li>Notre adresse email.</li>
<li>Un mot de passe pour sécuriser le certificat. Il faut le laisser vide, sinon StartSSL ne saura pas générer la clé publique.</li>
</ul>
<p>Si on regarde le fichier /etc/ssl/reqs/xyz.com.csr, il devrait contenir quelque chose comme ça&nbsp;:</p>
<pre>-----BEGIN CERTIFICATE REQUEST-----
... (plusieurs lignes encodées en base64) ...
-----END CERTIFICATE REQUEST-----</pre>
<p>Il faut le garder sous le coude, nous devrons ensuite le copier-coller dans l&#8217;interface de StartSSL.</p>
<h3>La vérification d&#8217;identité</h3>
<p>La procédure d&#8217;inscription sur le site StartSSL peut sembler un peu compliquée. Le site va générer un certificat qui va vous identifier personnellement. Ce certificat s&#8217;installe automatiquement sur votre navigateur (cela fonctionne avec Firefox mais pas avec Chrome)&nbsp;; si vous voulez utiliser un autre ordinateur, il faudra exporter le certificat sur le navigateur d&#8217;origine, et l&#8217;importer sur le navigateur de destination.</p>
<p>Ce certificat se contente de vous identifier. Pour le générer, il faut suivre la procédure du site, qui inclut la vérification de votre adresse email.</p>
<p>Une fois que l&#8217;on est connecté et identifié, on peut utiliser le &laquo;&nbsp;Validation Wizard&nbsp;&raquo; pour demander à valider notre identité en cliquant sur &laquo;&nbsp;Personal Identity Validation&nbsp;&raquo;. Cette validation nécessite que l&#8217;on scanne&nbsp; − ou que l&#8217;on prenne en photo − notre passeport (couverture, première page, page avec nom et photo) et une autre pièce d&#8217;identité comme la carte d&#8217;identité ou le permis de conduire (recto et verso). L&#8217;interface permet d&#8217;envoyer les fichiers correspondants. Dans la foulée, j&#8217;ai reçu un email me demandant une copie d&#8217;une facture récente de téléphone fixe ou mobile. Je me suis exécuté, et dans les minutes qui ont suivies j&#8217;ai reçu un coup de téléphone très aimable (en anglais)&nbsp;; pour vérifier mon identité, Eddy m&#8217;a demandé ma date et mon lieu de naissance.</p>
<p>En parallèle de ça, il faut aller dans l&#8217;onglet &laquo;&nbsp;Tool Box&nbsp;&raquo; de l&#8217;interface, pour enregistrer une carte bancaire ou un compte Paypal. Si vous choisissez Paypal, vous recevrez rapidement une demande de paiement correspondant aux 60$ facturés pour la vérification d&#8217;identité&nbsp;; si vous choisissez la carte bancaire, le prélèvement se fera automatiquement.</p>
<p>Notre identité est vérifiée, maintenant il faut prouver que l&#8217;on est bien propriétaire du domaine xyz.com. Pour cela, il faut retourner dans l&#8217;onglet &laquo;&nbsp;Validation Wizard&nbsp;&raquo;, en choisissant cette fois l&#8217;option &laquo;&nbsp;Domain Name Validation&nbsp;&raquo;. Après avoir saisi le nom xyz.com, on nous propose de choisir une adresse email à laquelle sera envoyé l&#8217;email de validation (postmaster@xyz.com, hostmaster@xyz.com ou webmaster@xyz.com). En menant la procédure à son terme, StartSSL enregistrera que le domaine nous appartient bien.</p>
<h3>Clé publique et certificat racine</h3>
<p>Nous pouvons maintenant générer la clé publique dont nous avons besoin pour sécuriser notre site. Il faut aller dans l&#8217;onglet &laquo;&nbsp;Certificates Wizard&nbsp;&raquo;, et choisir la cible &laquo;&nbsp;Web Server SSL/TLS Certificate&nbsp;&raquo;. La première étape doit être passée (bouton &laquo;&nbsp;Skip&nbsp;&raquo;). À l&#8217;étape suivante, il faut copier-coller le contenu du fichier /etc/ssl/reqs/xyz.com.csr. Ensuite, il faut ajouter le sous-domaine &laquo;&nbsp;*&nbsp;&raquo; à la liste de sous-domaines inclus dans le certificat (la liste est vide, il suffit simplement d&#8217;insérer l&#8217;étoile dans le champ texte et de passer à l&#8217;étape suivante). Après quelques dizaines de secondes, on reçoit un email indiquant que le certificat est disponible. Pour cela, il faut aller dans l&#8217;onglet &laquo;&nbsp;Tool Box&nbsp;&raquo;, puis dans &laquo;&nbsp;Retrieve Certificate&nbsp;&raquo;. Il faut sélectionner le certificat qui nous intéresse, puis le copier-copier dans le fichier /etc/ssl/certificates/xyz.com.crt (pensez à créer le répertoire d&#8217;abord).</p>
<p>Pour que notre certificat fonctionne, nous devons récupérer sur notre serveur deux fichiers supplémentaires&nbsp;: le certificat racine et le certificat intermédiaire.</p>
<pre>$&gt; mkdir /etc/ssl/roots /etc/ssl/chains
$&gt; wget https://www.startssl.com/certs/ca.pem \
   -O /etc/ssl/roots/xyz.com-root.ca.pem
$&gt; wget https://www.startssl.com/certs/sub.class2.server.ca.pem \
   -O /etc/ssl/chains/xyz.com.ca.pem</pre>
<h3>Configuration Apache</h3>
<p>Ça y est, nous avons récupéré tous les fichiers nécessaires. Ouf. Nous allons pouvoir configurer nos sites. StartSSL propose une <a href="http://www.startssl.com/?app=20" target="_blank">documentation succincte</a> pour la plupart des serveurs HTTP, je me suis inspiré de <a href="http://www.startssl.com/?app=21" target="_blank">celle pour Apache</a>.</p>
<p>Nous allons créer deux &laquo;&nbsp;virtual hosts&nbsp;&raquo; dans Apache. L&#8217;un répondra aux requêtes HTTPS, l&#8217;autre répondra aux requêtes HTTP et renverra vers l&#8217;équivalent sécurisé.</p>
<pre># sites sécurisés
&lt;VirtualHost *:443&gt;
 &nbsp;  ServerName      www.xyz.com
 &nbsp;  ServerAlias     *.xyz.com
 &nbsp;  DocumentRoot    /path/to/site
 &nbsp;  SSLEngine       on
 &nbsp;  SSLProtocol     all -SSLv2
 &nbsp;  SSLCipherSuite  ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM
 &nbsp;  SSLCertificateFile       /etc/ssl/certificates/xyz.com.crt
 &nbsp;  SSLCertificateKeyFile    /etc/ssl/private/xyz.com.key
 &nbsp;  SSLCertificateChainFile  /etc/ssl/chains/xyz.ca.pem
 &nbsp;  SSLCACertificateFile     /etc/ssl/roots/xyz.com-root.ca.pem
 &nbsp;  SetEnvIf    User-Agent   ".*MSIE.*"   nokeepalive \
        ssl-unclean-shutdown
 &nbsp;  # ... la suite de la configuration habituelle du virtual host
&lt;/VirtualHost&gt;

# sites non sécurisés
&lt;VirtualHost *:80&gt;
 &nbsp;  ServerName     www.xyz.com
 &nbsp;  ServerAlias    *.xyz.com
 &nbsp;  RewriteEngine  On
 &nbsp;  RewriteRule    ^(.*)$   https://%{HTTP_HOST}%{REQUEST_URI}
&lt;/VirtualHost&gt;</pre>
<p>Suivant la configuration existante, vous aurez peut-être besoin des directives suivantes (à placer dans le fichier précédent ou dans le fichier contenant le virtual host par défaut)&nbsp;:</p>
<pre>NameVirtualHost *:80
NameVirtualHost *:443</pre>
<p>On vérifie la configuration et on redémarre Apache&nbsp;:</p>
<pre>$&gt; apache2ctl configtest
$&gt; apache2ctl restart</pre>
<p>Il faut être attentif à ce qu&#8217;il se passe dans les fichiers de log, notamment dans /var/log/apache2/error.log</p>
<p>Si tout s&#8217;est bien passé, vous pourrez vous connecter à l&#8217;URL https://bidule.xyz.com sans problème. Et en allant sur http://machin.xyz.com/truc vous devriez être automatiquement redirigé vers http<strong>s</strong>://machin.xyz.com/truc</p>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2011/12/29/configuration-apache-en-ssl-avec-wildcard/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Faire des choix à la place de l&#8217;utilisateur</title>
		<link>http://www.geek-directeur-technique.com/2011/12/27/faire-des-choix-a-la-place-de-lutilisateur</link>
		<comments>http://www.geek-directeur-technique.com/2011/12/27/faire-des-choix-a-la-place-de-lutilisateur#comments</comments>
		<pubDate>Tue, 27 Dec 2011 15:14:24 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Création d'entreprise]]></category>
		<category><![CDATA[choix]]></category>
		<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Word]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=958</guid>
		<description><![CDATA[Il y a presque 2 ans, j&#8217;avais écrit un article intitulé simplement Faire des choix. Mon propos était alors de dire que lorsqu&#8217;on crée un service ou un produit, il faut faire en sorte que l&#8217;ergonomie (ou l&#8217;affichage, ou les fonctionnalités, &#8230;) proposée par défaut soit adaptée au plus grand nombre d&#8217;utilisateur. Plutôt que de [...]]]></description>
			<content:encoded><![CDATA[<p>Il y a presque 2 ans, j&#8217;avais écrit un article intitulé simplement <a title="Faire des choix" href="http://www.geek-directeur-technique.com/2010/01/14/faire-des-choix">Faire des choix</a>. Mon propos était alors de dire que lorsqu&#8217;on crée un service ou un produit, il faut faire en sorte que l&#8217;ergonomie (ou l&#8217;affichage, ou les fonctionnalités, &#8230;) proposée par défaut soit adaptée au plus grand nombre d&#8217;utilisateur.</p>
<p>Plutôt que de noyer l&#8217;utilisateur sous des options qui l&#8217;obligeront à choisir la manière dont il souhaite manipuler l&#8217;outil, il est préférable d&#8217;analyser les choses finement pour proposer quelque chose d&#8217;exploitable immédiatement et sans effort.</p>
<p>Il y avait eu un certain nombre de commentaires intéressants à l&#8217;époque, dont quelques-uns qui remettaient en question cette vision des choses. De manière assez amusante, je sais que certains de ces commentateurs sont par ailleurs dithyrambiques sur la manière dont Apple mène ses designs, justement en restreignant les options et en s&#8217;attachant à proposer les meilleurs choix par défaut&#8230;</p>
<p>Bref, je viens de tomber sur un lien très intéressant&nbsp;:<br />
<a href="http://www.uie.com/brainsparks/2011/09/14/do-users-change-their-settings/">http://www.uie.com/brainsparks/2011/09/14/do-users-change-their-settings/</a></p>
<p>L&#8217;histoire est édifiante. Afin de savoir si les utilisateurs modifient les options de leurs logiciels, ils ont demandé à des utilisateurs de Word d&#8217;envoyer le fichier de configuration présent sur leur machine. Plusieurs centaines ont répondu.<br />
Résultat&nbsp;: <strong>95% des utilisateurs utilisaient les réglages par défaut</strong>.</p>
<p>On pourrait penser que cela ne fait que montrer à quel point Microsoft avait bien fait son travail, et avait réussi à proposer un outil qui soit parfaitement fonctionnel pour le plus grand nombre. Et pourtant c&#8217;est complètement l&#8217;inverse&nbsp;!<br />
À cette époque, l&#8217;auto-enregistrement était désactivé par défaut. Si votre ordinateur plantait, vous perdiez tout votre travail, à moins que vous n&#8217;ayez activé cette option. Cette fonctionnalité était déjà perçue comme quelque chose de particulièrement utile. Malgré cela, l&#8217;écrasante majorité des utilisateurs ne faisaient pas l&#8217;effort de contraindre le logiciel à faire ce qu&#8217;ils voulaient.</p>
<p>Pourquoi&nbsp;? Parce qu&#8217;ils se disaient que Microsoft étant expert en la matière (ce qui semble normal, vu que c&#8217;est Microsoft qui édite le logiciel), il devait y avoir une bonne raison pour que cette fonctionnalité soit désactivée par défaut. Le problème n&#8217;était pas que les utilisateurs ne connaissaient pas l&#8217;existence de l&#8217;option&nbsp;; ils expliquaient ne simplement pas vouloir y toucher.</p>
<p>On pourra m&#8217;objecter que cette étude doit dater de plus de 10 ans. Quand bien même, je pense que le comportement des utilisateurs n&#8217;a pas changé d&#8217;un iota à ce niveau. Et on ne peut pas dire que Word soit un logiciel difficile à prendre en main&nbsp;; ce n&#8217;est pas un CRM ou un modeleur 3D.</p>
<p>Je ne le martèlerai donc jamais assez&nbsp;: Faites des choix à la place de vos utilisateurs.<br />
Si vous avez peur de vous tromper, commencez par vous mettre à leur place&nbsp;; utilisez votre produit, apprenez à le connaître&nbsp;; comparez avec les produits concurrents, faites le tri entre ce qui fonctionne quasiment tout le temps et la cerise sur le gâteau qui ne sera utilisée que par les power-users.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2011/12/27/faire-des-choix-a-la-place-de-lutilisateur/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Conférence à Epita le vendredi 13 janvier</title>
		<link>http://www.geek-directeur-technique.com/2011/12/14/conference-a-epita-le-vendredi-13-janvier</link>
		<comments>http://www.geek-directeur-technique.com/2011/12/14/conference-a-epita-le-vendredi-13-janvier#comments</comments>
		<pubDate>Wed, 14 Dec 2011 09:58:35 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Événements]]></category>
		<category><![CDATA[conférence]]></category>
		<category><![CDATA[epita]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=948</guid>
		<description><![CDATA[Dans le cadre de la semaine des projets professionnels, je présenterai ma conférence&#160;«De geek à directeur technique» aux étudiants d&#8217;Epita. Cela se tiendra dans les locaux de l&#8217;école au Kremlin Bicêtre, le vendredi 13 janvier, de 14h00 à 15h30. Cette conférence ne sera ouverte qu&#8217;aux étudiants de l&#8217;école. Venez nombreux&#160;!]]></description>
			<content:encoded><![CDATA[<p>Dans le cadre de la semaine des projets professionnels, je présenterai ma conférence&nbsp;«<em>De geek à directeur technique</em>» aux étudiants d&#8217;<a href="http://www.epita.fr/" target="_blank">Epita</a>.</p>
<p>Cela se tiendra dans les locaux de l&#8217;école au Kremlin Bicêtre, le vendredi 13 janvier, de 14h00 à 15h30.</p>
<p>Cette conférence ne sera ouverte qu&#8217;aux étudiants de l&#8217;école. Venez nombreux&nbsp;!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2011/12/14/conference-a-epita-le-vendredi-13-janvier/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ZeroMQ, file de messages et travail distribué</title>
		<link>http://www.geek-directeur-technique.com/2011/12/10/zeromq-file-de-messages-et-travail-distribue</link>
		<comments>http://www.geek-directeur-technique.com/2011/12/10/zeromq-file-de-messages-et-travail-distribue#comments</comments>
		<pubDate>Sat, 10 Dec 2011 12:34:14 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Non classé]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=928</guid>
		<description><![CDATA[Oui, je sais ce que vous allez me dire. En ce moment, je parle pas mal de ZeroMQ. Bah oui, c&#8217;est comme ça. Bref. Suite à mon dernier article sur le sujet, l&#8217;un de mes développeurs m&#8217;a fait une remarque très pertinente, à laquelle j&#8217;ai répondu en expliquant au passage quelques notions concernant le travail [...]]]></description>
			<content:encoded><![CDATA[<p>Oui, je sais ce que vous allez me dire. En ce moment, je parle pas mal de <a href="http://www.zeromq.org/" target="_blank">ZeroMQ</a>. Bah oui, c&#8217;est comme ça.</p>
<p>Bref. Suite à <a title="ZeroMQ et load-balancing&nbsp;: un exemple concret" href="http://www.geek-directeur-technique.com/2011/12/05/zeromq-et-load-balancing-un-exemple-concret">mon dernier article sur le sujet</a>, l&#8217;un de mes développeurs m&#8217;a fait une remarque très pertinente, à laquelle j&#8217;ai répondu en expliquant au passage quelques notions concernant le travail distribué.</p>
<h3>La situation</h3>
<p>Je replace rapidement le contexte&nbsp;: Nous développons un petit programme de sauvegarde, constitué d&#8217;un serveur et de &laquo;&nbsp;workers&nbsp;&raquo;. Le serveur envoie aux workers des ordres, pour leur indiquer les machines à sauvegarder. Grâce aux fonctionnalités de load-balancing intégrées à ZeroMQ, le serveur n&#8217;a pas à se soucier de&nbsp;«qui fait quoi»&nbsp;; il se contente d&#8217;envoyer les ordres, en se disant que les workers les recevront et les traiteront.</p>
<p>Le seul petit soucis est qu&#8217;il fallait prendre en compte le temps de création des workers, et le temps qu&#8217;ils mettent à se connecter au serveur. La solution que j&#8217;ai présenté est d&#8217;ajouter un canal de communication supplémentaire, qui permet aux workers d&#8217;indiquer au serveur qu&#8217;ils sont prêts.</p>
<p>Voici le schéma de l&#8217;infrastructure finale&nbsp;:</p>
<p><a href="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-2.png"><img class="alignnone size-medium wp-image-916" title="ZeroMQ Load-Balancing server-workers bidirectionnel" src="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-2-300x167.png" alt="" width="300" height="167" /></a></p>
<p>Les workers font du PUSH vers le serveur, pour lui confirmer leur disponibilité. Le serveur fait du PULL pour recevoir ces confirmations, puis fait du PUSH pour envoyer ses ordres, qui sont répartis entre les workers. Les workers font du PULL pour recevoir les ordres et les traiter.</p>
<h3>La solution alternative</h3>
<p>Mon développeur m&#8217;a alors demandé&nbsp;: &laquo;&nbsp;<em>Mais pourquoi les workers ne feraient pas du REQ pour demander au serveur le prochain ordre à exécuter, qu&#8217;il enverrait par REP&nbsp;?</em>&nbsp;&raquo;</p>
<p>Pour rappel, le REQ/REP (request/response) est l&#8217;un des 3 types de communications supportées par ZeroMQ. À chaque requête effectuée (du côté de la socket REQ), une réponse doit obligatoirement être envoyée (côté REP).</p>
<p>Effectivement, le schéma de l&#8217;architecture devient plus simple&nbsp;:</p>
<p><a href="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-3.png"><img class="alignnone size-full wp-image-930" title="ZeroMQ Load-Balancing REQ-REP" src="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-3.png" alt="" width="274" height="150" /></a></p>
<p>En faisant ainsi, on se soustrait la partie &laquo;&nbsp;initialisation&nbsp;&raquo;, pendant laquelle les workers indiquent au serveur qu&#8217;ils sont connectés et disponibles. Par contre, tout le reste du serveur se complexifie.</p>
<p>Dans un tel fonctionnement, les workers vont se connecter au serveur, pour lui demander&nbsp;«<em>Eh, qu&#8217;est-ce que je dois faire&nbsp;?</em>». Le serveur devra garder en mémoire une liste des machines à sauvegarder&nbsp;; à chaque fois qu&#8217;un worker fera une demande, il devra dépiler un nom de machine, pour l&#8217;envoyer dans sa réponse au worker.</p>
<p><span id="more-928"></span>Cela n&#8217;a rien de bien méchant, nous sommes d&#8217;accord. Juste une liste supplémentaire à gérer. Mais si on veut gérer une remontée d&#8217;information (que les workers préviennent le serveur quand ils ont terminé une tâche), il faudra là encore ajouter un peu de code supplémentaire.</p>
<p>Si on compare avec le fonctionnement précédent, on s&#8217;aperçoit d&#8217;une chose&nbsp;: En simplifiant l&#8217;architecture réseau, on complexifie le code applicatif&nbsp;; en complexifiant légèrement l&#8217;architecture réseau, on simplifie le code applicatif.</p>
<p>Toute la magie de ZeroMQ est là. Ajouter une socket ZMQ supplémentaire&nbsp;? C&#8217;est seulement deux lignes de code en plus&nbsp;!<br />
Il est donc plus simple de faire reposer la complexité sur ZeroMQ, le code n&#8217;en est que plus simple à comprendre, plus rapide à écrire, plus facile à maintenir.</p>
<h3>Une file de message</h3>
<p>Imaginons maintenant que nous voulions utiliser ZeroMQ pour mettre en place une file de message.</p>
<p>Je vais prendre un exemple concret. Dans mon entreprise, nous devons indexer des contenus, pour permettre leur recherche textuelle. Cette étape est relativement longue et coûteuse en CPU, elle ne peut donc pas être faite par les processus qui communiquent avec les navigateurs web au moment où les contenus sont créés.<br />
Les contenus sont donc enregistrés sans être indexés, et des messages sont ajoutés à une file de messages pour indiquer les contenus qui doivent être indexés.<br />
Sur une machine dédiée, un programme est exécuté à intervalles réguliers. Il consulte la file de messages pour savoir si de nouveaux contenus doivent être indexés.</p>
<p>La file de messages que nous utilisons est très frustre, basée sur une table en base de données. L&#8217;extension <a href="http://www.temma.net/fr/extensions/doc/FineMessageQueue/index.html" target="_blank">FineMessageQueue</a> disponible sur le site de <a href="http://www.temma.net/" target="_blank">notre framework Temma</a> en reprend le principe.</p>
<p>Si nous voulions la remplacer par une file de messages réalisée avec ZeroMQ, comment nous y prendrions-nous&nbsp;?</p>
<p>Pour commencer, nous devrions écrire un programme qui fera office de &laquo;&nbsp;serveur&nbsp;&raquo; (mettons de côté toute notion de redondance&nbsp;; pour nous simplifier la vie, on va faire une file centralisée).<br />
D&#8217;un côté, le serveur recevra des messages, qu&#8217;il ajoutera à sa file.<br />
La file de message pourrait être stockée uniquement en mémoire, mais c&#8217;est un peu risqué. Partons du principe que les messages sont stockés sur disque ou dans une base de données.<br />
De l&#8217;autre côté, le serveur va envoyer ses messages à des workers qui sont là pour ça et qui attendent qu&#8217;on leur demande de travailler.</p>
<p><a href="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-4.png"><img class="alignnone size-full wp-image-933" title="ZeroMQ Load-Balancing message-queue" src="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-4.png" alt="" width="503" height="201" /></a></p>
<p>Avec ce type d&#8217;architecture, il paraît évident que la communication entre le serveur et les workers ne va pas se faire par du REQ/REP. Si on imagine que les workers fassent un REQ pour savoir ce qu&#8217;ils ont à faire, et que la file de messages est vide, que se passe-t-il&nbsp;? Le serveur répond qu&#8217;il n&#8217;y a rien à faire&nbsp;; donc le worker ne &laquo;&nbsp;fait rien&nbsp;&raquo;. Et après&nbsp;? Il redemande au serveur ce qu&#8217;il a à faire. On tombe vite dans la boucle qui commence énormément de CPU pour rien.</p>
<p>Il est donc bien plus logique que ce soit le serveur qui informe les workers du travail qu&#8217;ils ont à faire. C&#8217;est un peu différent de la vision classique du client-serveur, mais c&#8217;est beaucoup plus efficace, et ZeroMQ permet d&#8217;y arriver très simplement.</p>
<h3>Une architecture complète</h3>
<p>Dans l&#8217;idée d&#8217;une telle file de messages, on considérera que les workers &laquo;&nbsp;sont là&nbsp;&raquo;. Ce n&#8217;est donc pas le serveur de file de messages qui va les démarrer. Cela implique potentiellement d&#8217;avoir des mécanismes dédiés à la gestion des workers&nbsp;: un démon se charge de lancer de nouveaux workers quand il n&#8217;y en a pas assez (dans une limite maximale définie), et les tue quand il y en a trop d&#8217;inactifs (dans une limite minimale définie).</p>
<p>Idéalement, le serveur de file de messages doit être capable de relancer une tâche qui n&#8217;a pas été déclarée comme terminée, après un certain délai. Ce qui veut dire que les workers doivent informer le serveur qu&#8217;ils ont terminé telle ou telle tâche.</p>
<p><a href="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-5.png"><img class="alignnone size-full wp-image-934" title="ZeroMQ Load-Balancing message-queue-workers" src="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-5.png" alt="" width="525" height="261" /></a></p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2011/12/10/zeromq-file-de-messages-et-travail-distribue/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>ZeroMQ et load-balancing : un exemple concret</title>
		<link>http://www.geek-directeur-technique.com/2011/12/05/zeromq-et-load-balancing-un-exemple-concret</link>
		<comments>http://www.geek-directeur-technique.com/2011/12/05/zeromq-et-load-balancing-un-exemple-concret#comments</comments>
		<pubDate>Mon, 05 Dec 2011 11:16:38 +0000</pubDate>
		<dc:creator>Amaury</dc:creator>
				<category><![CDATA[Questions techniques]]></category>
		<category><![CDATA[load-balancing]]></category>
		<category><![CDATA[sauvegarde]]></category>
		<category><![CDATA[zeromq]]></category>

		<guid isPermaLink="false">http://www.geek-directeur-technique.com/?p=912</guid>
		<description><![CDATA[Il y a 2 mois, j&#8217;ai écrit un article au sujet de ZeroMQ. Si vous ne l&#8217;avez pas encore lu, je vous le conseille, je pense avoir réussi à expliquer de manière assez simple les concepts de base de cette bibliothèque réseau aux fonctionnalités très puissantes. Pour joindre l&#8217;utile à l&#8217;agréable (comprendre&#160;: pour faire un [...]]]></description>
			<content:encoded><![CDATA[<p>Il y a 2 mois, j&#8217;ai écrit <a title="ZeroMQ, la super bibliothèque réseau" href="http://www.geek-directeur-technique.com/2011/09/21/zeromq-la-super-bibliotheque-reseau">un article au sujet de ZeroMQ</a>. Si vous ne l&#8217;avez pas encore lu, je vous le conseille, je pense avoir réussi à expliquer de manière assez simple les concepts de base de cette bibliothèque réseau aux fonctionnalités très puissantes.</p>
<p>Pour joindre l&#8217;utile à l&#8217;agréable (comprendre&nbsp;: pour faire un peu de <a title="La R&amp;D&nbsp;: pour ne pas confondre développement et veille techno" href="http://www.geek-directeur-technique.com/2009/08/31/la-rd-pour-ne-pas-confondre-developpement-et-veille-techno">R&amp;D</a>, monter mes équipes en compétence sur des projets intéressants pour l&#8217;entreprise), j&#8217;ai demandé à mon administrateur système de sortir de sa&nbsp;«zone de confort», et de coder un petit projet en PHP utilisant ZeroMQ.</p>
<h3>Le projet</h3>
<p>L&#8217;idée est de mettre en place un serveur de sauvegarde centralisé. Ce serveur contient un gros disque dur (en fait, plusieurs, mais j&#8217;y reviendrai), sur lequel est stocké une copie de tous les fichiers qui doivent être sauvegardés sur les postes de travail. Tous les jours, ce serveur doit lancer une série de <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Rsync" target="_blank">rsync</a> pour synchroniser le disque dur local avec les machines à sauvegarder. C&#8217;est l&#8217;étape de “backup”.</p>
<p>Par la suite, nous recopions les données sur un second disque dur. Chaque dimanche, une copie complète est effectuée, alors que tous les autres jours de la semaine on se contente de faire une copie incrémentale pour ajouter les nouveaux fichiers. C&#8217;est l&#8217;étape d&#8217;“archivage”.</p>
<p>(oui, je sais, on pourrait utiliser un logiciel de sauvegarde comme <a href="http://www.amanda.org/" target="_blank">Amanda</a>, mais l&#8217;aspect R&amp;D serait vachement moins évident, hein)</p>
<h3>La réalisation</h3>
<p>Tout cela n&#8217;a rien de bien sorcier. On pourrait faire un simple programme, lancé par <a title="Wikipedia" href="http://fr.wikipedia.org/wiki/Crontab" target="_blank">crontab</a>, qui lanceraient séquentiellement un rsync sur chaque poste de travail, puis qui effectuerait séquentiellement l&#8217;archivage de chaque machine. Le truc, c&#8217;est qu&#8217;en faisant ainsi, on perdrait un temps fou. De nos jours, on a des processeurs multi-cœurs, des réseaux gigabit ou plus&#8230; Ce serait quand même idiot de ne pas mener plusieurs sauvegardes en parallèle.</p>
<p>La première possibilité qu&#8217;on pourrait envisager serait simplement de lancer autant de programmes qu&#8217;il y a de machines à sauvegarder. Mais ce serait peut-être un peu violent et difficile à surveiller efficacement. Et plus le nombre de machine à sauvegarder augmente, moins cette méthode serait efficace.</p>
<p>On a donc décidé de mettre en place une architecture comprenant&nbsp; un serveur qui coordonne le travail de plusieurs &laquo;&nbsp;workers&nbsp;&raquo;, des programmes qui effectuent le boulot réel. L&#8217;idée est de démarrer un nombre fini de workers, ce qui détermine le nombre de tâches effectuées simultanément, et de leur indiquer les machines à sauvegarder.<br />
ZeroMQ excelle dans ce genre de situation. Les workers vont se connecter au serveur, et attendre qu&#8217;il leur envoie des ordres. Le serveur, lui se contentera d&#8217;envoyer des ordres séquentiels sur sa socket&nbsp;; ZeroMQ se chargera de les délivrer en les répartissant aux différents clients (c&#8217;est la fonctionnalité de load-balancing intégrée à ZeroMQ).</p>
<p><a href="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-1.png"><img class="alignnone size-medium wp-image-915" title="ZeroMQ Load-Balancing server-workers" src="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-1-300x141.png" alt="" width="300" height="141" /></a></p>
<p>Petit rappel&nbsp;: À la base, ZeroMQ fournit 3 types de communication. Le REQ/REP sert à faire du client-serveur classique (on fait une requête, on reçoit une réponse)&nbsp;; le PUSH/PULL pour envoyer des données à sens unique&nbsp;; le PUB/SUB pour envoyer des données à tous ceux qui s&#8217;y sont abonnés. La principale différence entre les deux derniers est que le PUB envoie ses paquets de données − en même temps − à tous les SUB qui y sont connectés, alors que le PUSH envoie ses données successivement à chacun des PULL connectés − l&#8217;un après l&#8217;autre.</p>
<h3>Le problème</h3>
<p>Mon admin sys. est revenu vers moi avec un comportement étrange. Quelque soit le nombre de workers, un seul d&#8217;entre eux recevait tous les paquets de données. Aïe.</p>
<p><span id="more-912"></span>Voici, en très simplifié, le code du serveur&nbsp;:</p>
<pre>// création de la socket ZMQ
$ctx = new ZMQContext();
$socket = new ZMQSocket($ctx, ZMQ::SOCKET_PUSH);
$socket-&gt;bind('tcp://*:1234');

// création des workers en tâche de fond
for ($i = 0; $i &lt; $nbrWorkers; $i++)
 &nbsp;  exec('/path/to/worker.php &gt;&gt; /path/to/log 2&gt;&amp;1 &amp;');

// envoi des ordres de sauvegarde
foreach ($machines as $machine)
 &nbsp;  $socket-&gt;send($machine);

// envoi des ordres de "suicide"
for ($i = 0; $i &lt; $nbrWorkers; $i++)
 &nbsp;  $socket-&gt;send('KILL');</pre>
<p>Le code du client (là encore, très simplifié)&nbsp;:</p>
<pre>// création de la socket ZMQ
$ctx = new ZMQContext();
$socket = new ZMQSocket($ctx, ZMQ::SOCKET_PULL);
$socket-&gt;connect('tcp://localhost:1234');

// traitement
while (true) {
 &nbsp;  // réception des données
 &nbsp;  $msg = $socket-&gt;recv();

 &nbsp;  // gestion du "suicide"
 &nbsp;  if ($msg == 'KILL')
 &nbsp; &nbsp; &nbsp;  exit(0);

 &nbsp;  // sauvegarde de la machine demandée
 &nbsp;  backup($msg);
}</pre>
<p>Reprenons le déroulement. Le serveur commence par créer sa socket ZMQ. Puis il crée des sous-processus pour instancier autant de workers que prévu. Dans la foulée, il envoie les noms des machines à sauvegarder. Puis il envoie autant d&#8217;ordres de &laquo;&nbsp;suicide&nbsp;&raquo; qu&#8217;il a créé de workers (pour leur demander de s&#8217;arrêter une fois que le travail est terminé).</p>
<p>Imaginons que nous avons cinq machines à sauvegarder (nommées A, B, C, D et E), et trois workers (nommés Prime, Seconde et Tierce).<br />
Le serveur envoie les messages dans l&#8217;ordre suivant&nbsp;: A, B, C, D, E, KILL, KILL, KILL.</p>
<p>On peut imaginer que les réceptions se fassent de la sorte&nbsp;:</p>
<ul>
<li>A =&gt; Prime</li>
<li>B =&gt; Seconde</li>
<li>C =&gt; Tierce</li>
<li>D =&gt; Prime</li>
<li>E =&gt; Seconde</li>
<li>KILL =&gt; Tierce</li>
<li>KILL =&gt; Prime</li>
<li>KILL =&gt; Seconde</li>
</ul>
<p>Tout irait bien&nbsp;; on peut voir que toutes les machines seraient sauvegardées en parallélisant les traitements (jusqu&#8217;à 3 sauvegardes simultanées), puis que chaque worker recevrait bien une instruction lui demandant de s&#8217;arrêter. Et pourtant, ce n&#8217;est pas le cas. L&#8217;un des workers reçoit tous les messages, et les deux autres rien du tout.</p>
<h3>L&#8217;explication</h3>
<p>ZeroMQ est une bibliothèque dont l&#8217;exécution est très rapide. Dans le code présenté ci-dessus, la partie la plus lente de l&#8217;exécution tient dans la connexion réseau&nbsp;; le moment où les socket BSD entrent en jeu pour connecter un programme à un autre.</p>
<p>En fait, au moment où le premier worker se connecte au serveur, ZeroMQ a déjà dans sa file d&#8217;attente interne tous les messages qui doivent être envoyés. Donc, dès que cette première connexion est établie, il lui balance tout. Ce qui est normal, car à ce moment-là il n&#8217;y a pas encore d&#8217;autre connexion avec laquelle faire le load-balancing.</p>
<p>La solution est donc d&#8217;attendre que toutes les connexions soient effectuées avant d&#8217;envoyer les données. Il y a deux manières d&#8217;y arriver&nbsp;; l&#8217;une est rapide mais crado, l&#8217;autre est bien plus propre mais un poil plus complexe.</p>
<h3>Méthode quick and dirty</h3>
<p>Le plus simple, pour bien comprendre où se situait le problème, est d&#8217;ajouter une temporisation entre la création des workers et l&#8217;envoi des données. Cela afin de laisser aux workers le temps de se connecter au serveur et d&#8217;être &laquo;&nbsp;enregistrés&nbsp;&raquo; dans le load-balancing.</p>
<p>Voici le code du serveur adapté&nbsp;:</p>
<pre>// création de la socket ZMQ
$ctx = new ZMQContext();
$socket = new ZMQSocket($ctx, ZMQ::SOCKET_PUSH);
$socket-&gt;bind('tcp://*:1234');

// création des workers en tâche de fond
for ($i = 0; $i &lt; $nbrWorkers; $i++)
 &nbsp;  exec('/path/to/worker.php &gt;&gt; /path/to/log 2&gt;&amp;1 &amp;');

// temporisation de 3 secondes
<strong>sleep(3);</strong>

// envoi des ordres de sauvegarde
foreach ($machines as $machine)
 &nbsp;  $socket-&gt;send($machine);

// envoi des ordres de "suicide"
for ($i = 0; $i &lt; $nbrWorkers; $i++)
 &nbsp;  $socket-&gt;send('KILL');</pre>
<p>Ah oui, je sais, c&#8217;est sale. Mais j&#8217;avais prévenu, et ça marche. Par contre, si on augmente le nombre de workers à lancer, on risque d&#8217;avoir une temporisation insuffisante. Et si la machine est spécialement lente à ce moment-là, on risque encore de rater des workers.</p>
<h3>Méthode propre</h3>
<p>Pour bien faire les choses, il faut que les workers envoient un message au serveur, pour lui signifier qu&#8217;ils sont prêts à recevoir des données. Ainsi, le serveur n&#8217;enverra ses ordres qu&#8217;après que tous les workers se soient déclarés.</p>
<p>Comme bien souvent avec ZeroMQ, cela impose d&#8217;ouvrir un canal de communication supplémentaire. Mais, comme toujours avec ZeroMQ, il ne faut pas avoir peur de le faire, car c&#8217;est simple et rapide à mettre en œuvre.</p>
<p>Cela donne donc une infrastructure de la forme suivante&nbsp;:</p>
<p><a href="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-2.png"><img class="alignnone size-medium wp-image-916" title="ZeroMQ Load-Balancing server-workers bidirectionnel" src="http://www.geek-directeur-technique.com/wp-content/uploads/2011/12/zmq-lb-2-300x167.png" alt="" width="300" height="167" /></a></p>
<p>Le serveur doit donc ouvrir deux sockets, l&#8217;une qui lui servira à envoyer ses ordres aux workers, l&#8217;autre pour recevoir les messages envoyés par ceux-ci.<br />
Cette seconde socket est d&#8217;ailleurs bien utile&nbsp;: elle permettra de remonter d&#8217;autres types d&#8217;informations, par exemple pour avertir le serveur à chaque fois qu&#8217;une sauvegarde est terminée.</p>
<p>Cela nous amène à un serveur qui ressemble à ceci&nbsp;:</p>
<pre>// création des sockets ZMQ
$ctx = new ZMQContext();
$output = new ZMQSocket($ctx, ZMQ::SOCKET_PUSH);
$output-&gt;bind('tcp://*:1234');
<span style="color: #000040;">$input = new ZMQSocket($ctx, ZMQ::SOCKET_PULL);</span>
<span style="color: #000040;">$input-&gt;bind('tcp://*:1235');</span>

// création des workers en tâche de fond
for ($i = 0; $i &lt; $nbrWorkers; $i++)
 &nbsp;  exec('/path/to/worker.php &gt;&gt; /path/to/log 2&gt;&amp;1 &amp;');

// réception des confirmations des workers
<span style="color: #000040;">for ($i = 0; $i &lt; $nbrWorkers; $i++)</span>
<span style="color: #000040;">    $input-&gt;recv();</span>

// envoi des ordres de sauvegarde
foreach ($machines as $machine)
 &nbsp;  $output-&gt;send($machine);

// envoi des ordres de "suicide"
for $i = 0; $i &lt; $nbrWorkers; $i++)
 &nbsp;  $output-&gt;send('KILL');</pre>
<p>Le code du client&nbsp;:</p>
<pre>// création des sockets ZMQ
$ctx = new ZMQContext();
$input = new ZMQSocket($ctx, ZMQ::SOCKET_PULL);
$input-&gt;connect('tcp://localhost:1234');
<span style="color: #000040;">$output = new ZMQSocket($ctx, ZMQ::SOCKET_PUSH);</span>
<span style="color: #000040;">$output-&gt;connect('tcp://localhost:1235');</span>

// envoi de la confirmation de connexion au serveur
<span style="color: #000040;">$output-&gt;send(1);</span>

// traitement
while (true) {
 &nbsp;  // réception des données
 &nbsp;  $msg = $input-&gt;recv();

 &nbsp;  // gestion du "suicide"
 &nbsp;  if ($msg == 'KILL')
 &nbsp; &nbsp; &nbsp;  exit(0);

 &nbsp;  // sauvegarde de la machine demandée
 &nbsp;  backup($msg);
}</pre>
<p>Et là, tout fonctionne à la perfection.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.geek-directeur-technique.com/2011/12/05/zeromq-et-load-balancing-un-exemple-concret/feed</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
	</channel>
</rss>

