WS-Discovery for WCF

Introduction

Windows Communication Foundation represents the state of the art communication library in the .NET environment. The flexibility of this device is amazing, and it allows to easily remotize functions on specific services, automatically serializing all the parameters in the signature through the simple decoration of properties and objects.

WCF allows to interconnect with services through web services standard communications or custom communications by simply modifying the configuration file, supporting a huge number of transfer protocols (HTTP, net TCP, MSMQ, ...) and communication protocols (security, reliable message...).

Another major quality of the library is its extensibility: it is possible to create new protocols, behaviors that can be defined at a service level or at an endpoint level.

The implementation of a service or of a service consumer through WCF is based on three concepts that are resumed by ABC: the Address where the service answers, the Binding which is how the service answers, and the Contract which are the methods exposed by the service. This article aims to describe a component which extends WCF in terms of configuration to make a service to be discovered at runtime by a service consumer through the WS-Discovery specification.

Background

WS-Discovery is a specification in the stack of WS-* protocols within the metadata.

StackProtocolli.gif

The WS-Discovery specification foresees that a service is characterized not by its ABC parameters, but only by the Contract and the 'scopes' that are useful information to distinguish two services having the same interface. Each service implements a scheme of scopes called ScopeMatchBy, and eventually, the scopes in URI format (Uniform Resource Identifier). A service can change its metadata, or the scopes themselves, during its life, but it has to keep track of the version through an information called MetadataVersion.

At the opening, the service introduces itself to the network through a multicast Hello message containing the above information. Similarly, at closure, it sends a multicast Bye message to the network. If the metadata or the scopes change, the service must send just a Hello message with the new MetadataVersion number.

The client looks for a service through the type (that is the contract), eventually attaching the information about scope and ScopeMatchBy. The search can be carried out in two ways: without discovery proxy, and with discovery proxy.

Without discovery proxy, the client sends a multicast message called Probe containing the search parameters (contract type, ScopeMatchBy, and scopes).

Probe-ProbeMatch.Png

The figure above shows the multicast communication in red and the unicast communication in blue.

Every service thinking of satisfying the request answers through a unicast ProbeMatch message. If ProbeMatch information is sufficient, the client can connect to the service, asking the service metadata to collect information about the binding first. Otherwise, the client must send a new multicast message called 'Resolve', and the service must answer with a unicast ResolveMatch message, giving necessarily the missing information.

If the discovery proxy is present in the network, the communication takes place with the same messages (Probe, ProbeMatch...), but modes change from multicast to unicast towards the discovery proxy.

To remind myself how communications take place, I introduce an analogy belonging to an environment other than the IT one. I think about several students in their classroom, every time a new student arrives, he enters the room greeting aloud and saying his name (Hello multicast). Suddenly, a student needs Robert, but he doesn’t’ know him. So, he calls Robert aloud in the silent room (Probe multicast). As a consequence, all the people called Robert stand up and go towards the person who called them (ProbeMatch unicast). Well, the discovery proxy represents the teacher in the room: when someone calls Robert aloud, besides all the people called Robert, the teacher stands up, too. He goes to the student and he introduces himself, 'I’m the teacher, and the next time you need someone, come and ask me for (Hello unicast). After that, the student stands up and asks the teacher when he needs someone (Probe unicast).

In this implementation, I won’t consider the version with the discovery proxy, but as soon as I have time, I will extend my code to include the discovery proxy as well, maybe integrating the IStorageAdapter of Roman Kiss’ WS-Transfer.

Using the code

The WS-Discovery implementation for WCF can be divided into two parts (into three parts if the discovery proxy implementation is included): a part concerning the service and the other concerning the client.

The integration of the discovery approach from the client side point of view extending the WCF configuration is problematic because WCF standard proxy works by specifying the Address Binding and Contract, while the discovery proxy needs only the Contract; other information are just discovered at runtime.

Moreover, I didn’t consider the client-side configuration aspect so important. The scopes are optional: if they aren’t specified, all services implementing the contract will satisfy the scopes request, unless particular service configurations are present.

The implementation of a discovery proxy doesn’t need to inherit from a class as per a WCF proxy, but it is enough to declare a DiscoveryClient< TChannel> class. In fact, the Channel property allows to interact with the methods of the remote service.

Collapse
DiscoveryClient<IServiceSample> proxy = new
 DiscoveryClient<IServiceSample>();

The constructor foresees to be able to eventually receive the scopes required for the service, and so it begins the search for the required service by sending a Probe message to the network. It is important to have an instance of the proxy as soon as possible so that at the first call of a proxy method, the service to be connected to has already been found avoiding useless waits.

ProbeDiscoveryClient.GIF

When the ProbeMatch message arrives, the client can release the WaitHandle , invoke the remote call, and return the result. If the client receives more than one ProbeMatch, the first one unlocks the semaphore, and at the moment of the call invocation, the best service is found through the virtual method GetBestMemento() . It is probable that at the first call, the first service who sent the ProbeMatch is used, unless a sufficient time went by between instantiation and method invocation to receive all ProbeMatch before the method invocation. Once the client uses a channel, it continues to use it all its life long. By re-creating the client, all the early stored ClientMemento remain, and so GetBestMemento will be able to find the best candidate in a more careful way.

As already said, GetBestMemento is a virtual function, that means that it is possible to inherit the DiscoveryClient< IServiceSample> class and to execute the overridden method to run the custom selection logic.

ClientMemento is the class that represents the basic information to interconnect to the service. Through the data received by the probe, it is possible to instantiate the memento that takes care of getting other possible information, as for instance the binding, through the metadata exposed by the service. If the client and the server use this discovery implementation, a shortcut is present to avoid the metadata roundtrip. The ProbeMatch message foresees the sending of the service address endpoint through the EndpointReference XML structure (see WS-Addressing), which includes not only an address field, but also an extensibility paradigm within the ReferenceParameters field. Within this field, the service enters all information concerning the communication binding. If there is this information, ClientM emento doesn’t need the metadata roundtrip, and it is ready for communication.

A major quality of the client implementation is the AutomaticChangeChannelWhenFaulted property, which allows the fault tolerance property on the services. In fact, when there are several candidate services, GetBestMemento returns a channel towards the chosen service. If the proxy operation fails, the client automatically repeats the same operation towards another candidate at disposal, and it throws the exception only when there is no other candidate on which the operation can be executed. This client functionality is disabled by setting the property to false; as a consequence, the exception would be thrown when other candidates are present as well.

One of the main worries dealing with the client concerned with the management of personalized headers in a message sent using WS-Discovery . WCF allows the addition of personalized headers by the use of the OperationContextScope class, which unfortunately was declared sealed and is therefore inextensible to discovery functionalities.

Collapse
IMyContract proxy = cf.CreateChannel();
using (new OperationContextScope((IClientChannel)proxy))
{
OperationContext.Current.OutgoingMessageHeaders.Add(
MessageHeader.CreateHeader(" otherHeaderName" ,
" http://otherHeaderNs" , " otherValue" ));
Console.WriteLine(proxy.echo(" Hello World" ));
}

The problem is due to the fact that the DiscoveryClient channel doesn’t implement the IClientChannel interface, at least until when the real channel is created at the end of the discovery process. That is why I had to create a less elegant solution that let nevertheless bypass the problem. I’m talking about the DiscoveryOperationContextScope< TChannel> class that allows, in a similar way, to execute the required operation.

Collapse
DiscoveryClient<IMyContract> proxy = new
 DiscoveryClient<IMyContract>();
using (DiscoveryOperationContextScope<IMyContract> os
= new DiscoveryOperationContextScope<IMyContract>(proxy))
{
os.OutgoingHeaders.Add(MessageHeader.CreateHeader(" MyHeaderName" ,
" " ," MyheaderValue" ));
Console.WriteLine(" Output string: " + proxy.Channel.GetString(" qqq" ));
}

The implementation of the service part seems easier, the behavior adds to the static ServiceContext class the information concerning the service. ServiceContext hooks the Opened and Closing events of ServiceHost to send Hello and Bye messages and it continues to listen to at the multicast port to receive the Probe/Resolve messages.

A function linked to the extensibility concerns the dynamic scopes. While implementing the service, I wanted to make the scopes to be dynamically manageable to let them be changed during the service life, reminding always to myself that every metadata change has be notified to the client through a new Hello message.

Configuration of the discoverable service

The part concerning the service foresees that WS-Discovery can be set during the configuration phase through a specific Behavior and an associated extension:

Collapse
<
extensions
>

< behaviorExtensions >
< add name =" serviceDiscoverableBehavior"
type =" Masieri.ServiceModel.WSDiscovery.Behaviors.DiscoveryBehaviorSection,
Masieri.ServiceModel.WSDiscovery, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=18ad931e67d285bd"

/ >
< / behaviorExtensions >
< / extensions >
< services >
< service behaviorConfiguration =" serviceDiscoverable"
name =" ServiceTest.IServiceSample" >
...

< / service >
< / services >

The discovery functionality setting can therefore be introduced without recompiling the code again but by simply introducing the settings in the configuration file. The associated behavior has to be configured with specific sections:

Collapse
<
behavior
 name
="
serviceDiscoverable"
>

< serviceMetadata
httpGetEnabled =" true"
httpGetUrl =" http://localhost:8080/Mex" / >
< serviceDiscoverableBehavior
scopesMatchBy =" http://schemas.xmlsoap.org/ws/2005/04/discovery/rfc2396" >
< scopes >
< add url =" http://myscope.tempuri.org/" / >
< / scopes >
< / serviceDiscoverableBehavior >
< / behavior >
< / serviceBehaviors >

The ServiceMetadata behavior has to be specified in the ServiceBehavior setting to allow to recover the binding settings through WSDL. Actually, this setting would be unnecessary, if this discovery library were used both for the client and for the server, as a mechanism was developed to avoid the metadata roundtrip and to allow a faster communication. The automatic metadata creation by WCF is very comfortable, but not so fast: it is true that it is created the first time and then kept in memory for the following requests; that is normal in 90% of the cases, but in some architectural cases, I found the services were often instantiated and destroyed with a lot of trouble for me as a consequence. If the WSDL creator had problems, for instance, due to unknown custom protocols (I noticed it in the implementation of the SDK soap.udp ), the creation could be slowed down for some seconds.

The service setting through the configuration is done, but it is possible to configure the service in a programmatic way.

Log

The log management was realized in a particularly attentive way to allow a complete integration with the WCF logs to have an exhaustive vision of the communication scenario. By the use of the configuration file, it is possible to add the System.ServiceModel.WSDiscovery source to the System.ServiceModel one and to make the log messages to flow together to a specific listener. The WS-Discovery messages are instead added to the System.ServiceModel.MessageLogging source as all the WCF messages.

Collapse
<
system.diagnostics
>

< sources >
< source name =" System.ServiceModel.WSDiscovery"
switchValue =" Warning, Error" >
< listeners >
< add initializeData =" InfoServiceDebug.e2e"
type =" System.Diagnostics.XmlWriterTraceListener,
System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"

name =" ServiceModel Listener"
traceOutputOptions =" LogicalOperationStack, DateTime,
Timestamp, ProcessId, ThreadId, Callstack"
/ >
< / listeners >
< / source >
< source name =" System.ServiceModel.MessageLogging"
switchValue =" Warning, Error" >
< listeners >
< clear / >
< add type =" System.Diagnostics.DefaultTraceListener" name =" Default"
traceOutputOptions =" None" / >
< add initializeData =" MessageLog.e2e"
type =" System.Diagnostics.XmlWriterTraceListener,
System, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"

name =" MessageLogging Listener"
traceOutputOptions =" LogicalOperationStack, DateTime, Timestamp,
ProcessId, ThreadId, Callstack"
/ >
< / listeners >
< / source >
< / sources >
< sharedListeners >
< add type =" System.Diagnostics.DefaultTraceListener"
name =" Default" / >
< / sharedListeners >
< / system.diagnostics >

Log.Png

Points of interest

In this paragraph, I would like to share my architectural experiences regarding the use of WS-Discovery .

Service redundancy

The key-aspect of an architecture based on WS-Discovery is the realisation of service redundancy when possible. Some time ago, I worked on the system of the Italian railway network (RFI). Talking about used solutions, it is as various and heterogeneous as every other system of such a great dimension, but the whole system is controlled through web services. The geographical aspect of the systems, which are located in stations, compartments, and in the RFI CED (data centre) was a peculiarity to be taken in an adequate consideration, because it was unconceivable that a web service could face all the calls coming from the whole country (actually, it is possible, but really expensive!).

In such a scenario, I developed the first version of WS-Discovery to try to completely change the existing model (of course, the sources were completely rewritten and improved in a lot of aspects). Working in a team of people who work well together, we developed a solution that made several clients from the whole territory, not to interface directly with the central web service, but to find run time local providers that gave them the same information. These local providers were actually cache managers pretending to be the required service. If the client needs information coming directly from the source, it can always get it by the use of the correct scope.

Scalar.Png

Fault tolerance

Fault tolerance represents another magic aspect of WS-Discovery . When a client cannot make a call to a service, it can try with another service satisfying the same scopes criteria. This behaviour is obviously settable through the client AutomaticChangeChannelWhenFaulted property.

Collapse
do

{
try
{
OnInvoking();
object ret = method.Invoke(_lastUsedChannel, parameters);
OnInvoked();
DiscoveryLogger.Info(" Method invoked successfully" );
return ret;
}
catch (Exception ex)
{
DiscoveryLogger.Error(" Errore nell'invocazione del servizio" , ex);
lock (_lock)
{
// endpoint error
ClientContext.Current.RemoveDiscoveredEndpoint(
Helpers.ContractDescriptionsHelper.GetContractFullName<TChannel>(), mem);
// Look for another one
mem = GetBestMemento();
if (mem == null )
{
// Start a new probe process for the future
StartProbeProcess();
DiscoveryLogger.Warn(
@" The DiscoveryClient can't scale on another service" );

// Now I can throw the exception
if (originalException == null )
throw ex;
else
throw originalException;
}
// I try again but I store the original exception before
if (originalException == null )
originalException = ex;
}
}
} while (AutomaticChangeChannelWhenFaulted);

Scopes with quality of service and dynamic change of the system

In another project developed together with the railway society, we studied a solution with dynamic scopes which represented the service QOS (Quality of service) to allow the client to hook the service whose QOS is compatible with its needs, without being too demanding, to avoid blocking all the services with better QOS. By executing the GetBestMemento overload, the client can select the service with the most suitable QOS, while the services update the QOS according to the number of simultaneous users through the MetadataVersion mechanism.

Extension of Roman Kiss’ ESB

WS-Discovery is completely compatible with the implementation of WS-Eventing and WS-Transfer dealt with in Roman Kiss’ article: server-side, by including the discovery Behavior into the service; client-side, by banally using the DiscoveryClient< RKiss.WSEventing.IWSEventing> class, for instance.

Dynamic load balancing

In a recent architecture for video surveillance and scene analyses systems, I implemented a service that can dynamically load some components of scene analyses which allowed, through a SubscriptionManager, to subscribe to the analyzed events. The computational work carried out by these components was high. I thought of integrating them in a server that was sufficiently scalable in several PCs, giving the identifier of the analyzed scene in the scopes. In such a way, the system can optimize the load on several machines without clustering configuration. It can dynamically distribute the load without taking care of the interconnected clients. In the case of dynamic reallocation of a component from a machine to another one, server 1 starts the host preparation process on a new machine (server 2), which sends a hello with the list of scopes of the served component when it is ready. The client, which receives the hello message, files the Memento among those at disposal. Server 1 sends a Hello with a new MetadataVersion version, where the scope of the removed component is no more present, and sends an EndSubscription message to the connected clients automatically, therefore causing the transfer to the new Memento and the new Subscribe on server 2.

EventListener.Png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于YOLOv9实现工业布匹缺陷(破洞、污渍)检测系统python源码+详细运行教程+训练好的模型+评估 【使用教程】 一、环境配置 1、建议下载anaconda和pycharm 在anaconda配置好环境,然后直接导入到pycharm,在pycharm运行项目 anaconda和pycharm安装及环境配置参考网上博客,有很多博主介绍 2、在anacodna安装requirements.txt的软件包 命令为:pip install -r requirements.txt 或者改成清华源后再执行以上命令,这样安装要快一些 软件包都安装成功后才算成功 3、安装好软件包后,把anaconda对应的python导入到pycharm即可(不难,参考网上博客) 二、环境配置好后,开始训练(也可以训练自己数据集) 1、数据集准备 需要准备yolo格式的目标检测数据集,如果不清楚yolo数据集格式,或者有其他数据训练需求,请看博主yolo格式各种数据集集合链接:https://blog.csdn.net/DeepLearning_/article/details/127276492 里面涵盖了上百种yolo数据集,且在不断更新,基本都是实际项目使用。来自于网上收集、实际场景采集制作等,自己使用labelimg标注工具标注的。数据集质量绝对有保证! 本项目所使用的数据集,见csdn该资源下载页面的介绍栏,里面有对应的下载链接,下载后可直接使用。 2、数据准备好,开始修改配置文件 参考代码data文件夹下的banana_ripe.yaml,可以自己新建一个不同名称的yaml文件 train:训练集的图片路径 val:验证集的图片路径 names: 0: very-ripe 类别1 1: immature 类别2 2: mid-ripe 类别3 格式按照banana_ripe.yaml照葫芦画瓢就行,不需要过多参考网上的 3、修改train_dual.py的配置参数,开始训练模型 方式一: 修改点: a.--weights参数,填入'yolov9-s.pt',博主训练的是yolov9-s,根据自己需求可自定义 b.--cfg参数,填入 models/detect/yolov9-c.yaml c.--data参数,填入data/banana_ripe.yaml,可自定义自己的yaml路径 d.--hyp参数,填入hyp.scratch-high.yaml e.--epochs参数,填入100或者200都行,根据自己的数据集可改 f.--batch-size参数,根据自己的电脑性能(显存大小)自定义修改 g.--device参数,一张显卡的话,就填0。没显卡,使用cpu训练,就填cpu h.--close-mosaic参数,填入15 以上修改好,直接pycharm运行train_dual.py开始训练 方式二: 命令行方式,在pycharm的终端窗口输入如下命令,可根据自己情况修改参数 官方示例:python train_dual.py --workers 8 --device 0 --batch 16 --data data/coco.yaml --img 640 --cfg models/detect/yolov9-c.yaml --weights '' --name yolov9-c --hyp hyp.scratch-high.yaml --min-items 0 --epochs 500 --close-mosaic 15 训练完会在runs/train文件下生成对应的训练文件及模型,后续测试可以拿来用。 三、测试 1、训练完,测试 修改detect_dual.py的参数 --weights,改成上面训练得到的best.pt对应的路径 --source,需要测试的数据图片存放的位置,代码的test_imgs --conf-thres,置信度阈值,自定义修改 --iou-thres,iou阈值,自定义修改 其他默认即可 pycharm运行detect_dual.py 在runs/detect文件夹下存放检测结果图片或者视频 【特别说明】 *项目内容完全原创,请勿对项目进行外传,或者进行违法等商业行为! 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值