SOA面向服务架构
2026/7/12 22:03:19 网站建设 项目流程

SOA相对于dds的主要区别是,SOA引入了服务和服务实例的概念,这就导致以下几点:

1、SOA规范服务发现怎么实现

2、dds中DomainParticipant、DataWriter、DataReader怎么和SOA中的服务、骨架、代理对应

3、通信的struct怎么实现,主要要考虑实例id

SOA是面向服务的通信架构,DDS属于DOA,面向数据的通信架构。

SOA即面向服务架构。虽然一开始我们对SOA面向服务架构的概念不了解,感觉很抽象。但是,当看到面向xxx的时候,是很熟悉的,讨论c++时,说c++是面向对象的编程语言。面相对象,那么对象作为一个基础的、核心的的概念,是c++的核心,c++中的概念都围绕着对象展开:类、抽象类、封装、继承、多态、模板等。那么对于SOA架构来说,则所有的概念都围绕着服务展开。

AP AUTOSAR标准基于SOA范式实现了通信模块(Specification of Communication Management),实现了面向服务的通信SOC(Service-Oriented Communication)。

本文以AP AUTOSAR(2311)通信规范文档为依据来讨论面向服务通信。

1SOC基本概念

本文中,服务端、骨架端是同一个概念;客户端,代理端是同一个概念。

面向服务通信,即通过服务这个基本概念把通信的概念涵盖进来。在自动驾驶中间件领域,即使我们完全没有听说过SOA的概念,当我们开发通信中间件的时候,不管使用dds还是ZMQ还是其它的通信中间件,我们常常要对中间件接口进行封装,实现如下的功能:

(1)发布-订阅

一侧发布数据,一侧订阅数据,比如原生dds就支持这种语义。

(2)远程调用

远程调用,可以像调用本地函数那样调用远程的函数,可以有入参和返回值。

也可以像setter和getter类似的功能,远程设置一个属性或者获取一个属性。

SOC通过服务这个核心概念,把我们常用的一些通信范式进行了统一,把常用的通信范式囊括了进来。SOC通过服务来提供发布-订阅、远程调用功能。当我们看到SOC的时候,完全不必纠结于面向服务到底是个什么概念,为什么叫面向服务,我们只需要知道面向服务,通过服务这个核心概念将发布-订阅、远程调用这些我们常见的通信范式进行了统一即可。

服务:

服务是核心概念,SOC中的所有概念都围绕着服务来展开。如上图所示,表示SOC的基本示意图,有服务的提供者,服务的需求者。服务的提供者提供服务,offer;服务的需求者查找服务,find;需求者找到服务之后便可以使用这个服务。

服务实例:

对于同一个服务,可以有一个服务端,也可以有多个服务端。每个服务端都是一个服务实例,服务实例用一个uint16类型的数据表示,叫做实例id。

对于同一个服务,可以有多个服务实例来提供这个服务。客户端可以通过实例id来查找并使用对应的服务。客户端可以指定一个要发现的服务,也可以指定发现该服务下的所有实例。

服务实例用于区分不同的服务端,不能用来区分客户端。SOA规范中也没有规定怎么区分不同的客户端。

骨架和代理:

骨架又称为服务端,代理又称为客户端,类似于tcp/udp的服务端和客户端。服务端提供服务,客户端使用服务。

event:

event是发布订阅的通信范式。在SOC中,只能服务端发布数据,客户端订阅数据,不能反过来

trigger:

简化版的event,没有具体数据。类似于进程间通信的socket和信号量,socket可以传输具体的数据,信号量只是负责将线程唤醒。event类似于socket, trigger类似于信号量。服务端发送trigger,客户端接收trigger。

method:

method即方法,在c++或者java中,类中声明的函数,一般也叫方法。SOC里的方法和c++类中的方法概念是类似的,都是调用一个函数,有形参和返回值,区别是c++中的方法是本地调用,SOC是远程调用。服务端作为方法的提供者,客户端可以调用。

field:

field即字段。在c++中,当我们定义一个类的属性,常常把属性定义为private,如果有在类外部访问这个属性的需求,比如读和写,那么便会声明对应的setter、getter方法。SOC中的field与c++类中的setter、getter类似,可以通过setter和getter远程设置或读取该字段的值。

2基于dds实现SOC

AP com提供了两种服务发现机制,一种是基于UserDataQos,一种是基于topic。

2.1服务发现

服务发现是通信的前提,不管是event、method、field、trigger哪种通信方式,只有发现服务,才能够进行具体的通信。

dds本身也有服务发现的机制,要经过PDP、EDP两个阶段进行服务发现,PDP是participant之间的相互发现,EDP是data writer和data reader之间的相互发现。

dds的服务发现过程,不需要用户参与,不需要用户调用什么接口,用户如果关心发现事件,那么可以通过dds提供的回调函数来监听。SOC不同,需要用户明确地调用接口来提供服务和查找服务,用户要参与到服务发现的过程中去,服务端要提供服务,只有提供服务之后,客户端才可以发现服务;客户端要查找服务,查找到服务之后要设置接收数据的回调函数,只能接收已查找到的服务。SOA提供了OffserService,StopOfferService,StartFindService,StopFindService等接口,用户可以对服务发现过程进行精确控制。

服务id,也就是服务的名字,服务实例id,服务的版本号。这3的元素,3元组用来描述一个服务。offer服务的时候需要提供3元组信息,find service的时候也需要提供这3个信息。

服务发现是通过domain participant发现服务的过程。对于客户端来说,只要发现了服务,就么就会返回handle,而不是data reader和data writer match了之后才返回handle。

FindService和StartFindService,前者是查找当前的快照,后者是持续监督,当发现有新服务,或者服务退出,均能被异步通知。

2.1.1基于UserDataQosPolicy的服务发现

UserDataQosPolicy参考:fastdds qos:UserDataQosPolicy-CSDN博客

用DomainPaticipant来代表一个服务实例。当服务端OfferService的时候,创建对应的DomainParticiapant,并为DomainParticiapant设置UserData,UserData的格式按下图所示。前缀是ara.com://services/,后边是服务id,服务实例id,版本号。如果多个服务实例共用一个DomainParticipant,那么UserData也可以设置多个服务实例的信息。

客户端也应创建DomainParticiapant,并当发现新的DomainParticiapant的时候,读取发现的DomainParticiapant的UserData,来判断该服务实例是不是自己所要找的服务实例。

fastdds qos:UserDataQosPolicy-CSDN博客

通过继承DomainParticipantListener,当有新的participant发现时,便可以获取participant的user data。服务端的domain participant设置user data,客户端的domain participant不需要设置。当客户端发现domain participant时,获取user data,通过解析user data中的service id和instance id,便可以确定服务是不是自己要找的服务。

2.1.1.1PartitionQos

如果服务端有两个,实例id分别是1和2。客户端有两个,客户端1和客户端2,分别需要服务端1和服务端2,那么客户端1只需要发现服务端1的DataWriter和DataReader,客户端2只需要发现服务端2的DataWriter和DataReader。虽然1和2的topic相同,data type也相同,但是实际使用中客户端1不需要发现服务端2,客户端2也不需要发现服务端1。这种使用需求,可以通过PartitionQos来实现。

默认情况下,所有在同一个participant,同一个topic下的data writer和data reader都可以相互发现,PartitionQos可以对topic进行分区,只有在同一个分区下的data writer和data reader才能相互发现。这种技术,实现了对topic的复用,类似的技术在很多场景中也有使用,比如为了节省资源,在tcp socket的基础上虚拟一层连接,多个虚拟连接复用同一个tcp连接。

客户端:

针对发现的每个服务实例,都创建一个Publisher和SubScriber,PartitionQos配置为PartitionQos。

服务端:

服务端也需要创建一个Publisher和Subscriber,并配置PartitionQos。

不管是服务端还是客户端,都需要针对每个服务实例创建对应的Publisher和Subscriber,并设置PartitionQosPolicy。

通过DomainParticipant的UserDataQosPolicy和DataWriter/DataReader下的PartitionQosPolicy,实现服务发现的过程。

2.1.3dds和SOC映射关系

1、当服务端调用OfferService

(1)用DomainParticipant代表一个服务实例。domain id和qos profile用来标记一个domain participant,如果有相同的服务实例,domain id和qos profile相同,那么服务实例可以使用同一个participant。

(2)针对每个event,创建一个topic和DataWriter

(3)针对每个trigger,创建一个topic和DataWriter

(4)针对method,创建一个request topic和一个reply topic,服务端要创建request topic的DataReader和reply topic的DataWriter。

通过一个requect topic和reply topic实现远程调用功能。这是最简单,也是最先想到的一种实现方式。在没有接触SOC标准的时候,自己也能想到这样的实现方式,但是总感觉这种方式不太优雅,太low了。

服务端创建request topic的data reader和reply topic的data writer;客户端则创建request topic的data writer和reply topic的data reader。

所有创建topic的地方,都需要检查是不是已经创建了,同一个participant,如果已经创建了,那么不能重复创建。

(5)field处理

①如果field中包含notifier,notifier与event类似,那么需要创建一个topic和DataWriter

②当hadGetter或hasSetter,创建一个request topic和一个reply topic,服务端要创建request topic的DataReader和reply topic的DataWriter

(6)为Domainparticipant设置user data

2、当服务端调用StopOfferService

(1)将user data从Domainparticipant的qos中移除。将user data移除之后,即使该daomin participant再被发现,也不会获取到该服务的信息,也就相当于停止提供服务了。

(2)移除在OfferService时创建的DataWriter和DataReader;topic可能共享,所以topic要进行判断,没有使用者的时候才可以销毁。

3、当客户端调用FindService、StartFindService

(1)绑定一个BuiltinParticipantListener

(2)查找已经发现的DomainParticipant,根据客户端配置的关注的服务实例,向用户返回对应服务实例的handle

4、当客户端调用StopFindService

解绑BuiltinParticipantListener

2.1.2基于topic的服务发现机制

基于topic进行服务发现,则需要内部创建一个topicara.com://services/discovery,该topic的data type为ServiceAnnouncementMessage。每个进程都要创建该topic以及对应的DataWriter和DataReader。ServiceAnnouncementMessage不是周期性发送,那么怎么保证新上线的DataReader能发现在之前上线的DataWriter发布的服务信息呢,需要按如下配置DataWriter的qos。DataWriter配置DurabilityQosPolicy为TRANSIENT_LOCAL,则发送的事件会进行保存,新上线的DataReader也能获取到历史数据。

HISTORY的KEEP_LAST的DEPTH配置为1。每个interface_id和instance_id够成一个key,来创建一个instance。

当identifier type设置为SERVICE_INSTANCE_RESOURCE_TOPIC_PREFIX,则服务端和客户端的Publisher和Subscriber不需要设置PartitionQos,而是会通过topic名来进行分区划分。实际使用的topic名格式如下:

ara.com://services/<InterfaceID>/<InstanceID>/<TopicName>

当identifier_type设置为SERVICE_INSTANCE_RESOURCE_PARTITION,则需要对Publisher和Subscriber设置Partition Qos。

2.2event

(1)struct名字在用户的名字之后加了后缀EventType

(2)struct字段增加了instance_id

@key,topic instance,以instance_id作为key来标记instance。

2.3trigger

(1)struct名是固定的TriggerType。不像event,每个event的数据是有区别的,所以每个event的struct是不一样的;而trigger只是一个信号,不需要包含用户信息,所以trigger的struct可以固定。

(2)struct成员只有一个,即实例id

2.4method

RequestHeader和ReplyHeader均是omg定义的规范https://www.omg.org/spec/DDS-RPC/1.0/PDF。RequestHeader中instanceName即服务实例的实例id,requestId是DataWriter的guid。DataWriter的guid在RequestHeader和ReplyHeader中均有,当调用方法的一方收到reply之后,根据DataWriter的guid来判断,request是不是自己发送的,如果是,则将返回值返回给用户;否则,不返回。

2.5field

field中的notifier与event类似,只不过struct命名有区别。。

setter、getter使用的data type,与method类似。同样的,struct命名有区别。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询