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
Dans le document
Vers l'Auto-Optimisation dans les Systèmes Autonomes.
(Page 179-184)