• Aucun résultat trouvé

Applications réelles de FructOz et LactOz

Nous démontrons dans cette section comment combiner le canevas FructOz

spé-cialisé dans la construction des systèmes distribués fondés sur des architectures

dy-namiques avec la bibliothèque LactOz de calcul et de navigation dynamique. Nous

utilisons de façon conjointe le canevas FructOz et la bibliothèque LactOz pour décrire

des systèmes distribués fondés sur des architectures dynamiques qui mettent en œuvre

des systèmes réels. Dans cet objectif, nous reprenons les différents contextes

applica-tifs que nous avons considérés dans le cadre de l’implantation de politiques

d’auto-optimisation : les services à messages, les services Internet et les services de surveillance

des systèmes distribués.

11.3.1 Service à messages en grappe

Les services à messages en grappe ont une architecture qui correspond exactement à

celle d’un composant redimensionnable (voir la section6.2.1, page68et la section7.1.3,

page85). En supposant que nous disposons d’un composant représentant un serveur

JMS nomméJMSPkg, nous pouvons construire un service à messages en grappe en nous

appuyant sur le composant générique redimensionnableResizablePkgprésenté en

sec-tion9.4.2(page125). L’architecture que nous décrivons ci-dessous est paramétrée par

l’ensembleClusterdes machines à utiliser pour déployer les instances des serveurs JMS,

par le package du composantJMSPkgreprésentant un serveur JMS et par des

spécifica-tions de qualité de serviceQoSSpecsassociées à l’architecture (comme par exemple une

liste de contraintes, dont un nombre maximal de clients que l’architecture doit pouvoir

supporter, etc).

fun{MessageService Cluster JMSPkg QoSSpecs}

%% Simple host allocator: we assume here that JMSPkg components are tagged with ’JMS’

SHosts= {CGetSubComponents Cluster}

SFreeHosts= {SFilter SHosts

fun{$ Host} {SIsEmpty{SFilter{CGetSubComponents Host} {TaggedWith’JMS’}}}end}

%% GetHost randomly chooses a host from the free hosts list

fun{GetHost}

LHosts= {SFreeHosts toList($)}

in

ofnilthen raisenoMoreResourcesend

[]H|TthenH

end

end

%% Deploy a sub−component instance on a random free host

fun{ClusterDeploy Package}

Host= {GetHost}

in

{RemoteDeploy Host Package}

%% The Host component is automatically removed from the free host list SFreeHosts.

end

%% Remove an existing sub−component and release the corresponding host

proc{ClusterUndeploy Comp}

Host= {SGetSingleton{SFilter{CGetSuperComponents Comp} {TaggedWith’host’}}}

in

%% Removing the component instance requires to remove its reference from the Host component.

%% The component instance will then be collected on the next GC.

%% As a result, the Host component will be considered free again in SFreeHosts.

{CRemoveSubComponent Host Comp}

end

%% Capacity planning

%% Determine the correct number of JMS servers matching the given QoS constraints

NJMSServers= {JMSCapacityPlanning QoSSpecs}

in

%% Resizable architecture

{ResizablePkg JMSPkg NJMSServers ClusterDeploy ClusterUndeploy}

end

Le composantJMSPkgpeut directement implanter un serveur JMS en Oz, ou bien

en-capsuler un serveur JORAM dont il contrôle la configuration et l’exécution, par exemple

en invoquant des commandes shell grâce à la fonction système fourni par la plate-forme

Mozart :{OS.system command}.

Cette description s’appuie sur une fonction réalisant la planification de la capacité

du service à messages en grappeJMSCapacityPlanning. Cette fonction, qui n’est pas

préci-sée ici, peut par exemple exploiter une modélisation des serveurs JMS afin d’en

déter-miner le nombre d’exemplaires nécessaires pour satisfaire les spécifications de qualité

de service indiquées, comme par exemple un nombre de clients.

11.3.2 Service Internet en grappe

De façon similaire, l’architecture d’un service Internet en grappe correspond à un

pipeline de composants redimensionnables (voir la section 6.2.2, page 70 et la

sec-tion 7.1.3, page 87). En nous inspirant de l’architecture exhibant un schéma

d’inter-connexion dynamique entre deux composants redimensionnables que nous avons

pré-sentée en section9.4.2(page127), nous construisons l’architecture d’un service Internet

composé de trois étages. Comme dans l’exemple précédent, l’architecture décrite

divers composants et par une spécification de qualité de serviceQoSSpecs. L’architecture

est également paramétrée par les descriptions (packages) des trois types de serveurs

correspondant aux trois étages du service à mettre en œuvre :WebPkg,AppPkgetDBPkg,

ainsi que par deux schémas d’interconnexion intervenant respectivement entre l’étage

Web et l’étage applications, et entre l’étage applications et l’étage bases de données.

fun{InternetService Cluster WebPkg AppPkg DBPkg WebAppBindScheme AppDBBindScheme QoSSpecs}

%% Simple host allocator

...% similar to the previous example (MessageService)

%% Determine the correct number servers matching the given QoS constraints

NWebServers NAppServers NDBServers

{JavaEECapacityPlanning QoSSpecs NWebServers NAppServers NDBServers}

in

functor

exportMembrane

define

Comp= {CNew nil}

%% Pipeline of resizable components

WebSizable= {Deploy{ResizablePkg WebPkg NWebServers ClusterDeploy ClusterUndeploy}}

{CAddSubComponent Comp WebSizable}

AppSizable= {Deploy{ResizablePkg AppPkg NAppServers ClusterDeploy ClusterUndeploy}}

{CAddSubComponent Comp AppSizable}

DBSizable= {Deploy{ResizablePkg DBPkg NDBServers ClusterDeploy ClusterUndeploy}}

{CAddSubComponent Comp DBSizable}

%% Locate the set of processing units of each resizable component

SWebServers= {SFilter{CGetSubComponents WebSizable} {TaggedWith’Processing Unit’}}

SAppServers= {SFilter{CGetSubComponents AppSizable} {TaggedWith’Processing Unit’}}

SDBServers= {SFilter{CGetSubComponents DBSizable} {TaggedWith’Processing Unit’}}

%% Apply the dynamic interconnection scheme

{ApplyBindingScheme WebAppBindScheme SWebServers SAppServers}

{ApplyBindingScheme AppDBBindScheme SAppServers SDBServers}

Membrane=Comp

end

end

De façon analogue, les composantsWebPkg,AppPkgetDBPkgpeuvent implanter des

composants d’encapsulation Jade des systèmes patrimoniaux, par exemple d’un

ser-veur Apache HTTPD pourWebPkg, d’un serveur Apache Tomcat pourAppPkg, etc. Ces

composants pourraient aussi directement implanter en Oz les fonctionnalités qu’ils

re-présentent, comme par exemple un serveur Web ou un serveur de bases de données.

Nous pouvons réutiliser le schéma d’interconnexionDynamicFullInterconnectprésenté en

section9.4.2 mettant en œuvre une interconnexion dynamique totale entre deux

11.3.3 Service de surveillance des systèmes distribués

Nous montrons ici comment décrire l’architecture hiérarchique d’un service de

sur-veillance d’un système distribué (voir section6.2.3, page72). Cette architecture intègre

des composants SensorPkg implantant les sondes du système de surveillance, et des

composantsAggregatorPkgqui agrègent plusieurs sources d’informations. Une sonde

ef-fectue périodiquement de nouvelles mesures sur la machine sur laquelle elle est

dé-ployée. Chaque machine à surveiller sera ainsi équipée d’une sonde, et chaque machine

correspondant à un nœud non terminal de l’architecture hiérarchique (i.e. qui n’est pas

une feuille de l’arbre) accueillera également un agrégateur dont les sources

d’informa-tions sont la sonde présente sur la machine ainsi que les agrégateurs présents sur les

nœuds correspondant aux branches issues du nœud.

Pour le déploiement, nous utilisons une fonctionMakeTreequi génère un arbre

pa-ramétré par son arité et par un ensemble de machines. L’arbre généré est représenté

par des enregistrementsNode=node(host:Host branches:Branches)pour lesquelsNode.host

dé-signe un composant représentant une machine etNode.branchesest un tuple

(éventuel-lement vide) listant les nœuds issus du nœud Node. La procédure récursiveDeployTree

parcourt la structure d’arbre ainsi générée, déploie les différents composants sur les

machines en fonction de leur position dans l’architecture hiérarchique et établit les

liai-sons nécessaires. La figure11.2illustre l’architecture hiérarchique que nous mettons en

œuvre ici sur un arbre binaire de 7 machines. Notons que tous les composants (sondes

et agrégateurs) du service de surveillance sont placés dans un même composant

com-posite représentant l’ensemble du système de surveillance. Ce comcom-posite n’est pas

re-présenté sur la figure.

Host1

S

A

Host2

A

S

Host3

A

S

Host4

S

Host5

S

Host6

S

Host7

S

FIG. 11.2 – Architecture hiérarchique du service de surveillance. Sur cet exemple, il

s’agit d’un arbre binaire s’étendant sur 7 machines. Les composants A désignent les

agrégateurs, et les composantsSdésignent les sondes.

fun{MonitoringService SHosts QoSSpecs}

Arity Depth Tau

{TreeMonitoringCapacityPlanning SHosts QoSSpecs Arity Depth Tau}

%% Compute a tree from the given set of hosts

TreeHosts= {MakeTree SHosts Arity}

in

functor

exportMembrane

define

Comp= {CNew nil}

proc{DeployTree ParentNode Node}

Host=Node.host

Branches=Node.branches

%% Deploy a sensor locally, start it, and ask it for a new client interface

Sensor= {RemoteDeploy Host SensorPkg}

{CAddSubComponent Comp Sensor}

{{IResolve{CGetInterface Sensor[sensor control]}}start}

IClient= {{IResolve{CGetInterface Sensor[sensor control]}}addClient($)}

in

if(ParentNode==nil)then% Root node: deploy an aggregator locally

Aggregator= {RemoteDeploy Host AggregatorPkg}

{CAddSubComponent Comp Aggregator}

%% Start the aggregator and bind it the to local sensor

{{IResolve{CGetInterface Aggregator[sensor control]}}start}

Binding= {BNew IClient{CGetInterface Aggregator[sensor input]}}

in

%% Deploy node branches

{Record.forAll Branchesproc{$ Branch} {TreeDeploy Node Branch}end}

else% Internal or leaf node: get the parent aggregator

ParentHost=ParentNode.host

ParentAggregator= {SGetSingleton

{SFilter{CGetSubComponents ParentHost} {TaggedWithAll[monitoring aggregator]}}}

IParentAggregatorInput= {CGetInterface ParentAggregator[sensor input]}

in

if({Record.width Children} == 0)then% Leaf node: no local aggregator, use the parent’s one

%% Bind the local sensor to the parent aggregator

Binding= {BNew IClient IParentAggregatorInput}

else% Internal node: deploy an aggregator locally

Aggregator= {RemoteDeploy Host AggregatorPkg}

{CAddSubComponent Comp Aggregator}

IAggregatorClient= {{IResolve{CGetInterface Aggregator[sensor control]}}addClient($)}

%% Start the periodic aggregator, bind it to the parent aggregator and to the local sensor

{{IResolve{CGetInterface Aggregator[sensor control]}}start}

Binding1= {BNew IAggregatorClient IParentAggregatorInput}

Binding2= {BNew IClient{CGetInterface Aggregator[sensor input]}}

in

%% Deploy node branches

{Record.forAll Branchesproc{$ Branch} {TreeDeploy Node Branch}end}

end

end

end

{TreeDeploy nil TreeHosts}

Membrane=Comp

end

end