НИЖЕГОРОДСКИЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ им. Р.Е.Алексеева Дисциплина "Робтотехника" Отчет по лабораторной работе № 2 Тема: "Распределённые вычисления" Вариант 9 Выполнил студент группы 10-В-1 Сидоренко О.О. Проверил: Гай В.Е. Н. Новгород 2013 г. Постановка задачи Требуется разработать распределённую систему для решения вычислительной задачи. Такая система должна включать сервисы двух типов: координирующий (MainService) и вычислительный (CompService). Требования к координирующему сервису: 1) существует только один экземпляр координирующего сервиса; 2) координирующий сервис запускает вычислительные сервисы на узлах, раздаёт им задания и собирает результаты вычислений с каждого узла, завершает вычислительные сервисы. Требования к вычислительному сервису: 1) на одной ЭВМ может быть запущено несколько DSS узлов для запуска вычислительных сервисов; 2) вычислительный сервис принимает задание, выполняет вычисления и отправляет результат обработки на координирующий сервис; Задание: Разработать алгоритм умножения матрицы a (m × n элементов) на n вектор b (n элементов) по следующей формуле: ci aijb j ,1 i m . При j 1 разработке параллельного алгоритма необходимо учитывать, что значение i-го элемента вектора c не зависит от значений других элементов вектора 2 Листинг. Координирующий сервис. //MainService.cs using using using using using using using using System; System.Collections.Generic; System.ComponentModel; Microsoft.Ccr.Core; Microsoft.Dss.Core.Attributes; Microsoft.Dss.ServiceModel.Dssp; Microsoft.Dss.ServiceModel.DsspServiceBase; W3C.Soap; using using using using using submgr = Microsoft.Dss.Services.SubscriptionManager; compSrv = CompService.Proxy; ds = Microsoft.Dss.Services.Directory; cs = Microsoft.Dss.Services.Constructor; System.Xml; namespace MainService { [Contract(Contract.Identifier)] [DisplayName("MainService")] [Description("MainService service (no description provided)")] class MainService : DsspServiceBase { /// <summary> /// Service state /// </summary> [ServiceState] MainServiceState _state = new MainServiceState(); /// <summary> /// Main service port /// </summary> [ServicePort("/MainService", AllowMultipleInstances = false)] MainServiceOperations _mainPort = new MainServiceOperations(); [SubscriptionManagerPartner] submgr.SubscriptionManagerPort _submgrPort = new submgr.SubscriptionManagerPort(); /// <summary> /// Service constructor /// </summary> public MainService(DsspServiceCreationPort creationPort) : base(creationPort) { } int c = 2; string[] names; // compSrv.CompServiceOperations[] _compSrvPort; compSrv.CompServiceOperations[] _compSrvNotify; compSrv.Subscribe[] subscribe; Port<int> complPort = new Port<int>(); ds.DirectoryPort[] remoteDir; cs.ConstructorPort[] remoteConstructor; ds.Query[] query; ds.QueryResponseType[] queryRsp; cs.Create[] create; CreateResponse[] createRsp; string[] compService; XmlQualifiedName[] namex; PartnerType[] remote; /// <summary> /// Service start /// </summary> protected override void Start() { if (_state == null) _state = new MainServiceState(); base.Start(); 3 names = System.IO.File.ReadAllLines(@"K:\Lab2\main.txt"); _compSrvPort = new compSrv.CompServiceOperations[c]; _compSrvNotify = new compSrv.CompServiceOperations[c]; subscribe = new compSrv.Subscribe[c]; remoteDir = new ds.DirectoryPort[c]; remoteConstructor = new cs.ConstructorPort[c]; query = new ds.Query[c]; queryRsp = new ds.QueryResponseType[c]; create = new cs.Create[c]; createRsp = new CreateResponse[c]; compService = new string[c]; namex = new XmlQualifiedName[c]; remote = new PartnerType[c]; for (int i = 0; i < c; i++) { _compSrvPort[i] = new compSrv.CompServiceOperations(); _compSrvNotify[i] = new compSrv.CompServiceOperations(); } for (int i = 0; i < c; i++) // обработчик Replace сообщения, полученного от вычислительного сервиса Activate<ITask>( Arbiter.Receive<compSrv.Replace>(true, _compSrvNotify[i], RemoteNotifyReplaceHandler) ); Arbiter.Activate(Environment.TaskQueue, Arbiter.MultipleItemReceive( true, complPort, c, delegate(int[] array) { LogInfo("Коорд. серв.: данные от выч. серв. получены"); for (int i = 0; i < 10; i++) LogInfo(_state.c[i].ToString()); // отправка сообщений на завершение вычислительным сервисам for (int i = 0; i < c; i++) { _compSrvPort[i].DsspDefaultDrop(); _compSrvPort[i] = null; } })); ///////////////////////////////////////////// //// начало формирования состояния сервиса, в котором находится вся информация о вычислительной задаче // PC, port // номера портов должны соответствовать соглашению указанному: различаться на 1 только. ///////////////////////////////////////////// // формируется список узлов, на которых будет запущен вычислительный сервис _state.NodeList.Add(new MainServiceState.NodeElement(names[0], Convert.ToInt32(names[1]), 0)); _state.NodeList.Add(new MainServiceState.NodeElement(names[2], Convert.ToInt32(names[3]), 1)); // формируется вычислительное задание для каждого узла _state.ClArr = new HelpClass[c]; int a1 = 10; // строки int b1 = 10; // столбцы for (int i = 0; i < c; i++) _state.ClArr[i] = new HelpClass(); _state.ClArr[0].a = 0; _state.ClArr[0].b = a1 / 2 - 1; _state.ClArr[1].a = a1 / 2; _state.ClArr[1].b = a1 - 1; _state.A = new int[a1, b1]; _state.B = new int[b1]; _state.c = new int[a1]; for (int i = 0; i < a1; i++) { for (int j = 0; j < b1; j++) _state.A[i, j] = i + j; } for (int j = 0; j < b1; j++) _state.B[j] = j; 4 // запускается вычислительный процесс SpawnIterator(OnStartup); } /// <summary> /// Handles Subscribe messages /// </summary> /// <param name="subscribe">the subscribe request</param> // этот обработчик обрабатывает запросы на подписку, отправляемые 7 сервису от 4 сервиса // при получении запроса на подписку выполняется сама подписка, далее // 7 сервис отправляет 4 своё состояние, в котором находится вся информация о вычислительной задаче [ServiceHandler(ServiceHandlerBehavior.Concurrent)] public IEnumerator<ITask> SubscribeHandler(Subscribe subscribe) { SubscribeRequestType request = subscribe.Body; LogInfo("Коорд. серв.: запрос подписки от: " + request.Subscriber); // использование Subscription Manager для управления подписками yield return Arbiter.Choice( SubscribeHelper(_submgrPort, request, subscribe.ResponsePort), delegate(SuccessResult success) { // Send a notification on successful subscription so that the // subscriber can initialize its own state base.SendNotificationToTarget<Replace>(request.Subscriber, _submgrPort, _state); }, delegate(Exception e) { LogError(null, "Коорд. серв.: ошибка при подписке к выч. серв.", e); } ); yield break; } // итератор OnStartup запускает на указанном узле вычислительные сервисы, // выполняет подписку координирующего сервиса на сообщения вычислительных сервисов private IEnumerator<ITask> OnStartup() { string[] nms = new String[c]; string[] rmt = new String[c]; nms[0] nms[1] rmt[0] rmt[1] = = = = "http://schemas.tempuri.org/2013/05/mainservice.html:Remote"; "http://schemas.tempuri.org/2013/05/mainservice.html:Remote"; "http://"+names[0]+":"+names[1]+"/directory"; "http://"+names[2]+":"+names[3]+"/directory"; // Это позволяет управлять любым количеством сервисов for (int i = 0; i < c; i++) { namex[i] = new XmlQualifiedName(nms[i]); remote[i] = new PartnerType(namex[i], null, rmt[i]); } /////// тут начало цикла, хотя можно было сделать и раньше for (int i = 0; i < c; i++) { remoteDir[i] = DirectoryPort; if (remote[i] != null && !string.IsNullOrEmpty(remote[i].Service)) { remoteDir[i] = ServiceForwarder<ds.DirectoryPort>(remote[i].Service); } remoteConstructor[i] = ConstructorPort; query[i] = new ds.Query( new ds.QueryRequestType( new ServiceInfoType(cs.Contract.Identifier) ) ); remoteDir[i].Post(query[i]); yield return (Choice)query[i].ResponsePort; queryRsp[i] = query[i].ResponsePort; if (queryRsp[i] != null) { 5 remoteConstructor[i] = ServiceForwarder<cs.ConstructorPort>(queryRsp[i].RecordList[0].Service); } compService[i] = null; create[i] = new cs.Create(new ServiceInfoType(compSrv.Contract.Identifier)); remoteConstructor[i].Post(create[i]); yield return (Choice)create[i].ResponsePort; createRsp[i] = create[i].ResponsePort; if (createRsp[i] != null) { compService[i] = createRsp[i].Service; } else { LogError((Fault)create[i].ResponsePort); yield break; } _compSrvPort[i] = ServiceForwarder<compSrv.CompServiceOperations>(compService[i]); yield return _compSrvPort[i].Subscribe(_compSrvNotify[i], out subscribe[i]); if ((Fault)subscribe[i].ResponsePort != null) LogError("Коорд. серв.: ошибка при подписке на уведомления выч. серв.", (Fault)subscribe[i].ResponsePort); else LogInfo("Коорд. серв.: подписка на уведомления выч. серв. выполнена"); } } // обработчик, запускаемый при получении Replace сообщения от вычислительного сервиса private void RemoteNotifyReplaceHandler(compSrv.Replace replace) { // объединяем результаты, полученные от вычислительного сервиса // и записываем их в результирующий массив int id = replace.Body._id; int _aa1 = _state.ClArr[id].a; int _bb1 = _state.ClArr[id].b; LogInfo("Коорд. серв.: получены данные от " + replace.Body._id + " __ " + _aa1 + " to " + _bb1); for (int i = _aa1; i <= _bb1; i++) { _state.c[i] = replace.Body._c[i]; LogInfo(replace.Body._c[i].ToString()); } // отправка в порт завершения сообщения о том, что пришли данные с одного из // вычислительных сервисов complPort.Post(1); } /// <summary> /// обработчик Replace сообщения /// </summary> [ServiceHandler(ServiceHandlerBehavior.Exclusive)] public IEnumerator<ITask> ReplaceHandler(Replace replace) { _state = replace.Body; // отправка сообщения Replace всем подписчикам base.SendNotification(_submgrPort, replace); replace.ResponsePort.Post(DefaultReplaceResponseType.Instance); yield break; } [ServiceHandler(ServiceHandlerBehavior.Teardown)] public IEnumerator<ITask> DropHandler(DsspDefaultDrop drop) { base.DefaultDropHandler(drop); yield break; } } } //MainServiceTypes.cs using System; 6 using using using using using using using System.Collections.Generic; System.ComponentModel; Microsoft.Ccr.Core; Microsoft.Dss.Core.Attributes; Microsoft.Dss.ServiceModel.Dssp; Microsoft.Dss.ServiceModel.DsspServiceBase; W3C.Soap; namespace MainService { /// <summary> /// MainService contract class /// </summary> public sealed class Contract { /// <summary> /// DSS contract identifer for MainService /// </summary> [DataMember] public const string Identifier = "http://schemas.tempuri.org/2013/05/mainservice.html"; } // вспомогательный класс // используется в качестве типа списка для хранения параметров [DataContract] public class HelpClass { [DataMember] public int a; // начало диапазона [DataMember] public int b; // конец диапазона } /// <summary> /// MainService state /// </summary> [DataContract] public class MainServiceState { // описание структуры данных, рассылаемой вычислительным узлам // здесь указывается идентификатор для узла и порта/ // на одном компьютере могут запускаться несколько узлов [DataContract] public struct NodeElement { [DataMember] public string pc; [DataMember] public int port; [DataMember] public int id; public NodeElement(string p1, int p2, int p3) { pc = p1; port = p2; id = p3; } } private List<NodeElement> _NodeList = new List<NodeElement>(); [DataMember(IsRequired = true)] public List<NodeElement> NodeList { get { return _NodeList; } set { _NodeList = value; } } // массивы которые отправляются на вычислительные сервисы [DataMember(IsRequired = true)] public int[,] A; [DataMember(IsRequired = true)] public int[] B; [DataMember(IsRequired = true)] public int[] c; 7 // массив параметров [DataMember(IsRequired = true)] public HelpClass[] ClArr; } /// <summary> /// MainService main operations port /// </summary> [ServicePort] public class MainServiceOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Subscribe, Replace> { } /// <summary> /// MainService get operation /// </summary> public class Get : Get<GetRequestType, PortSet<MainServiceState, Fault>> { /// <summary> /// Creates a new instance of Get /// </summary> public Get() { } /// <summary> /// Creates a new instance of Get /// </summary> /// <param name="body">the request message body</param> public Get(GetRequestType body) : base(body) { } /// <summary> /// Creates a new instance of Get /// </summary> /// <param name="body">the request message body</param> /// <param name="responsePort">the response port for the request</param> public Get(GetRequestType body, PortSet<MainServiceState, Fault> responsePort) : base(body, responsePort) { } } /// <summary> /// MainService subscribe operation /// </summary> /// public class Subscribe : Subscribe<SubscribeRequestType, PortSet<SubscribeResponseType, Fault>> { } /// <summary> /// MainService replace operation /// </summary> public class Replace : Replace<MainServiceState, PortSet<DefaultReplaceResponseType, Fault>> { } } 8 Листинг. Вычислительный сервис. //CompService.cs using System; using System.Collections.Generic; using System.ComponentModel; using Microsoft.Ccr.Core; using Microsoft.Dss.Core.Attributes; using Microsoft.Dss.ServiceModel.Dssp; using Microsoft.Dss.ServiceModel.DsspServiceBase; using W3C.Soap; //операторы для управления сервисом using submgr = Microsoft.Dss.Services.SubscriptionManager; using mainSrv = MainService.Proxy; using ds = Microsoft.Dss.Services.Directory; using cs = Microsoft.Dss.Services.Constructor; using System.Net; using Microsoft.Dss.Core; namespace CompService { [Contract(Contract.Identifier)] [DisplayName("CompService")] [Description("CompService service (no description provided)")] class CompService : DsspServiceBase { /// <summary> /// Service state /// </summary> [ServiceState] CompServiceState _state = new CompServiceState(); /// <summary> /// Main service port /// </summary> [ServicePort("/CompService", AllowMultipleInstances = true)] CompServiceOperations _mainPort = new CompServiceOperations(); [SubscriptionManagerPartner] submgr.SubscriptionManagerPort _submgrPort = new submgr.SubscriptionManagerPort(); /// <summary> /// Service constructor /// </summary> public CompService(DsspServiceCreationPort creationPort) : base(creationPort) { } mainSrv.MainServiceOperations _mainSrvPort = new mainSrv.MainServiceOperations(); mainSrv.MainServiceOperations _mainSrvNotify = new mainSrv.MainServiceOperations(); mainSrv.Subscribe subscribe = new mainSrv.Subscribe(); /// <summary> /// Service start /// </summary> protected override void Start() { // // Add service specific initialization here // if (_state == null) _state = new CompServiceState(); base.Start(); //в итераторе запускается подписка на уведомления коорд. сервиса SpawnIterator(OnStartup); Activate<ITask>(Arbiter.Receive<mainSrv.Replace>(true, _mainSrvNotify, RemoteNotifyReplaceHandler)); } 9 ServiceInfoType[] list = null; private IEnumerator<ITask> OnStartup() { string[] names = System.IO.File.ReadAllLines(@"K:\Lab2\comp.txt"); // подключение к узлу // ip-адрес string machine = names[0]; // TCP порт для передачи HTTP запросов ushort nport = Convert.ToUInt16(names[1]); // создание URI сервиса UriBuilder builder = new UriBuilder(Schemes.DsspTcp, machine, nport, ServicePaths.InstanceDirectory); ds.DirectoryPort fport = ServiceForwarder<ds.DirectoryPort>(builder.Uri); //создание и отправка Get-запроса ds.Get get = new ds.Get(); fport.Post(get); //обработка результата запроса yield return Arbiter.Choice(get.ResponsePort, delegate(ds.GetResponseType response) { list = response.RecordList; }, delegate(Fault fault) { list = new ServiceInfoType[0]; LogError(fault); } ); //количество найденных сервисов int cnt = 0; //массив, хранящий информацию об найденных сервисах ServiceInfoType[] servList = new ServiceInfoType[cnt]; foreach (ServiceInfoType info in list) { if (info.Contract == mainSrv.Contract.Identifier) { Array.Resize(ref servList, servList.Length + 1); servList[cnt] = info; cnt += 1; } } _mainSrvPort = ServiceForwarder<mainSrv.MainServiceOperations>( servList[0].Service); subscribe.NotificationPort = _mainSrvNotify; //отправка запроса на подписку _mainSrvPort.Post(subscribe); //обработка результата запроса yield return Arbiter.Choice( subscribe.ResponsePort, delegate(SubscribeResponseType response) { LogInfo("Выч. серв. подписан на " + servList[0].Service); }, delegate(Fault fault) { LogError("Выч. серв. " + fault); } ); yield break; } private void RemoteNotifyReplaceHandler(mainSrv.Replace replace) { LogInfo("Выч. серв.: получено Replace сообщение от коорд. сервиса"); // определение идентификатора экземпляра вычислительного сервиса Dictionary<string, string> env = Environment.ServiceUriTable; string addr = " "; foreach (KeyValuePair<string, string> pair in env) { addr = pair.Value; break; 10 } string[] splitAddr = addr.Split('/'); string pcinfo = splitAddr[2]; string[] info = pcinfo.Split(':'); string pc = info[0]; string port = info[1]; int id = -1; foreach (mainSrv.MainServiceState.NodeElement xx in replace.Body.NodeList) { if (((xx.port + 1).ToString() == port) && (xx.pc.ToString() == pc)) { id = xx.id; break; } } LogInfo("Выч. серв.: идентификатор сервиса: " + id.ToString()); int aa1 = replace.Body.ClArr[id].a; int bb1 = replace.Body.ClArr[id].b; c = new int[replace.Body.c.Length]; Port<int> p = new Port<int>(); Receiver r = Arbiter.Receive(false, p, delegate(int n) { Replace resReplace = new Replace(); _state._c = c; _state._id = id; resReplace.Body = _state; base.SendNotification(_submgrPort, resReplace); }); Activate(r); // формирование вычислительного задания HelpStruct pr = new HelpStruct(replace.Body.ClArr[id].a, replace.Body.ClArr[id].b, replace.Body.b, replace.Body.A); // запуск задачи на выполнение Arbiter.ExecuteToCompletion(Environment.TaskQueue, new IterativeTask<Port<int>, HelpStruct>(p, pr, CalcHandler)); } public struct HelpStruct { public int _a, _b; public int[] B; public int[,] A; public HelpStruct(int p1, int p2, int[] p3, int[,] p5) { _a = p1; _b = p2; B = p3; A = p5; } } // переменная, хранящая результат вычислений int[] c; //выполняет вычисления на основе задания от коорд. сервиса IEnumerator<ITask> CalcHandler(Port<int> port, HelpStruct p) { for (int i = p._a; i <= p._b; i++) { c[i] = 0; for (int j = 0; j < p.B.Length; j++) { c[i] += p.A[i, j] * p.B[j]; } LogInfo("C ===" + c[i].ToString()); } port.Post(1); 11 yield break; } [ServiceHandler(ServiceHandlerBehavior.Teardown)] public virtual IEnumerator<ITask> DropHandler(DsspDefaultDrop drop) { Console.WriteLine("Выч. серв. завершил свою работу"); base.DefaultDropHandler(drop); //ControlPanelPort.Shutdown(); yield break; } /// <summary> /// Handles Subscribe messages /// </summary> /// <param name="subscribe">the subscribe request</param> [ServiceHandler(ServiceHandlerBehavior.Concurrent)] public IEnumerator<ITask> SubscribeHandler(Subscribe subscribe) { SubscribeRequestType request = subscribe.Body; LogInfo("Выч. серв.: запрос подписки от: " + request.Subscriber); //здесь также используется менеджер подписок yield return Arbiter.Choice( SubscribeHelper(_submgrPort, request, subscribe.ResponsePort), delegate(SuccessResult success) { LogInfo("Выч. серв.: Подписка выполнена"); }, delegate(Exception e) { LogError(null, "Выч. серв.: подписка не выполнена", e); } ); yield break; } /// <summary> /// Обработчик Replace сообщения /// </summary> [ServiceHandler(ServiceHandlerBehavior.Exclusive)] public IEnumerator<ITask> ReplaceHandler(Replace replace) { _state = replace.Body; base.SendNotification(_submgrPort, replace); replace.ResponsePort.Post(DefaultReplaceResponseType.Instance); yield break; } } } //CompServiceTypes.cs using using using using using using using using System; System.Collections.Generic; System.ComponentModel; Microsoft.Ccr.Core; Microsoft.Dss.Core.Attributes; Microsoft.Dss.ServiceModel.Dssp; Microsoft.Dss.ServiceModel.DsspServiceBase; W3C.Soap; namespace CompService { /// <summary> /// CompService contract class /// </summary> public sealed class Contract { /// <summary> /// DSS contract identifer for CompService /// </summary> [DataMember] public const string Identifier = "http://schemas.tempuri.org/2013/05/compservice.html"; } /// <summary> 12 /// CompService state /// </summary> [DataContract] public class CompServiceState { // результат вычислений [DataMember] public int[] _c; // идентификатор вычислительного сервиса [DataMember] public int _id; } /// <summary> /// CompService main operations port /// </summary> [ServicePort] public class CompServiceOperations : PortSet<DsspDefaultLookup, DsspDefaultDrop, Get, Subscribe, Replace> { } /// <summary> /// CompService get operation /// </summary> public class Get : Get<GetRequestType, PortSet<CompServiceState, Fault>> { /// <summary> /// Creates a new instance of Get /// </summary> public Get() { } /// <summary> /// Creates a new instance of Get /// </summary> /// <param name="body">the request message body</param> public Get(GetRequestType body) : base(body) { } /// <summary> /// Creates a new instance of Get /// </summary> /// <param name="body">the request message body</param> /// <param name="responsePort">the response port for the request</param> public Get(GetRequestType body, PortSet<CompServiceState, Fault> responsePort) : base(body, responsePort) { } } /// <summary> /// CompService subscribe operation /// </summary> public class Subscribe : Subscribe<SubscribeRequestType, PortSet<SubscribeResponseType, Fault>> { } /// <summary> /// CompService replace operation /// </summary> public class Replace : Replace<CompServiceState, PortSet<DefaultReplaceResponseType, Fault>> { } } 13 Результат работы программы Вычислительный сервис №1: Вычислительный сервис №2: Координирующий сервис: 14 Отладочные сообщения вычислительного сервиса №1: Отладочные сообщения вычислительного сервиса №2: 15 Отладочные сообщения координирующего сервиса: 16