文章目录
一句话总结:这玩意是AA客户端用的服务交互代理。AA通过调用这个东西的方法,来和服务交互。
概念
Proxy类是从ARXML直接生成的。不是人写的。这部分要花钱买协议栈。
好像也没啥要说的
构造
供应商的工具只负责给你生成一个类。AA的开发人员要自己实例化一个对象来用。
在proxy类里面,有一个公有类成员:
class HandleType {
inline bool operator==(const HandleType &other) const;
const ara::com::InstanceIdentifier &GetInstanceId() const;
};
这个类的对象,会作为构造proxy传入的参数。
我这么说你能不能理解:
proxy只能通过传入handle来构建,handle是调用FindService这个COM的API来返回的。handle里面包含了诸如通讯地址信息之类的归CM管且AA开发人员不应该关心的技术细节。
这里可能有人会问:为什么中间要封一层,调用服务发现直接返回一个proxy实例不行吗?整个handle干嘛。
原因1:proxy实例里面会包含state信息,这个我们希望能和通讯本身解耦。不用handle,大家都状态一样了。
原因2:一个服务实例外面我们希望挂很多proxy。如果不用handle,那就只有一个proxy了。
最后,proxy类不允许复制构造,强制用户从handletype构造。防止无意间复制了状态、缓存等敏感信息造成混乱。
Finding Services
说到SOA,就开始讲什么服务化。那要用服务,就有个服务发现的过程。
Proxy类提供一个static方法来实现服务发现,寻找服务实例。所谓静态类方法,就是所有类成员公用的一套东西。实际上感觉不需要实例化这个类也可以通过域作用符来调用该方法。
AP讲究的是“变化”。那么就不能指望服务一直存在。服务也有自己的生命周期。(我感觉可能和FC的调度有关了)。ara::com给了两个方法来发现服务:
StartFindService
后台轮询,持续调用“FindService”FindService
找一次
这些方法喂的参数有三种:
- ara::com::InstanceIdentifier
- ara::core::InstanceSpecifier
- 无参
没参数就是我全都要的意思。不管是什么技术绑定,都给我找回来。
这里我有一个疑问,是不是意味着:我看起来是找服务,实际上是在找建立好的信道。就是所谓的technical bingdings。这些信道会在服务接口部署(集成人员干的事情)manifest里面定义好,但是我要用的时候是否存在却不知道。需要“发现”来找一下。
FindService
返回一个container of handles
。然后再基于这个handle构建proxy. 返回空的就说明没找到。
static ara::com::ServiceHandleContainer<RadarServiceProxy::HandleType>
FindService(
ara::core::InstanceSpecifier instanceSpec);
static ara::com::FindServiceHandle StartFindService(
ara::com::FindServiceHandler<RadarServiceProxy::HandleType> handler,
ara::com::InstanceIdentifier instanceId);
StartFindService
返回FindServiceHandle
,这个东西就是用来通过StopFindService
调用停止后台持续进行的FindService。StartFindService
方法的第一个参数是一个用户定义的函数:
using FindServiceHandler = std::function<void(ServiceHandleContainer<T>, FindServiceHandle)>;
每当binding检测到StartFindService发现的服务实例发生变化,上述这个handler函数就会被和更新后的handles list一起被调用。注意,每次服务实例的可用性发生变更,都会重新调用这个handler。
AP标准允许用户在handler里面调用StopFindService。这样,可以理解为什么handler需要FindServiceHandle这个参数。不要求handler设计成可重入的。
那这么看起来,Find和Stop应该是要闭环成对使用的,这个要求供应商做在binding里了?
请注意,当用作 FindService 的返回值或用作 FindServiceHandler 的参数时,ServiceHandleContainer 可以实现为分配或非分配容器,只要它满足 C++ 编程语言的一般和序列容器要求。
Proxy实例的自动更新
服务有他自己的生命周期,一会死了又活了。我们希望proxy在服务复活后是可重用的。一般来讲,在服务导向的系统中,服务的提供和消费者在整个系统的生命周期内就是周期性的启停。所以在服务发现的基础设置里面,我们做了一套监控机制。
这里好像没细说啊 就说了下表现和好处。
Events
在生成的proxy命名空间下,会生成events namespace。 event都被封装成了proxy 的成员类。
要获得服务实例通过event推送的数据。需要先subscribe(订阅)events。告诉CM,我要这些events的。
void Subscribe(size_t maxSampleCount);
maxSampleCount
是告诉CM AA这边保持的最大样本数量。然后在本地开一个cache接收数据。这个cache交给CM来填,用户不用管。
要获取订阅状态:
ara::com::SubscriptionState GetSubscriptionState() const;
- 还没好:
kSubscriptionPending
- 好了:
kSubscribed
调用这个接口也常被用来检测服务实例是否还存在。
两种方法来监控订阅状态:
1、轮询GetSubscriptionState
2、注册一个handler 订阅状态变更就调用。
下面是第二种方式 的实现:
void SetSubscriptionStateChangeHandler(ara::com::
SubscriptionStateChangeHandler handler);
enum class SubscriptionState { kSubscribed, kNotSubscribed,
kSubscriptionPending };
2 using SubscriptionStateChangeHandler = std::function<void(
SubscriptionState)>;
这样,一旦订阅状态变化,CM就调用上面的handler。一般我们需要在订阅(调用subscribe)之前就先向CM登记handler。
在收到 “subscribe order”之后,CM会先调用一次handler(喂参数SubscriptionState.kSubscriptionPending)。如果之后收到服务端的反馈,再调用handler(喂参数SubscriptionState.kSubscribed.)
proxy的更新、更新后的订阅event的重新注册。调用state change handler都是交给CM来自动执行。不需要管。
从接收event数据的buffer里提取数据的API:
/** 2 * \brief Get new data from the Communication Management
* buffers and provide it in callbacks to the given callable f. 4 * 5 * ....
*/ 7 template <typename F>
ara::core::Result<size_t> GetNewSamples(
F&& f,
size_t maxNumberOfSamples = std::numeric_limits<size_t>::max());
maxNumberOfSamples是最大event样本量。f是抓取buffer数据后调用的函数。
我觉得应该是每抓取一组调用一次f,传给f指向获取本的指针(SamplePtr)。让f来进行后续处理(留下还是扔了之类的。)
GetNewSamples最后返回调用f的次数(那不就是采集的样本个数吗)。
GetNewSamples并不是得到时间来了之后就调用的API,CM在检测到event更新后会调用的API是下面这个:
void SetReceiveHandler(ara::com::EventReceiveHandler handler);
这一句是向CM注册:新的event来了之后调用 上面这个 handler来处理(可以在这个handler里面封装一个GetNewSamples来取数据)。
当然 如果你周期调用GetNewSamples,就是轮询事件数据了。
不想事件触发了可以向CM撤销注册的handler
UnsetReceiveHandler()
下面来讲讲evens数据的buffer策略。因为AP都是有AA内存保护和隔离的,所以buffer不是说存在service的私有空间之类的。而是放在由kernel管理的空间或者共享内存或者交给专门的守护进程管理。service发给中介,然后多个proxy从这个中介去取。
另外,AP建议的是传递所谓的"reference"而不是“copy values”。就比如说前面传递的都是指向数据container的指针而不是数据本身。在app本地的cache存储的也可以是指向CM管理的buffer的指针(reference)。
Methods
对于每个服务提供的method,proxy class都有一个对应的封装类。生成在methods namespace下。proxy::methods。
One-way aka(as known as) Fire and Forget Methods。只有输入参数没有输出参数,也不会引发error。与server无握手无同步。client也不会收到反馈。
Method也有轮询和触发的调用方式。通过ara::core::Future来实现。
事件驱动在Method里的意味是:Method的调用者在调用Method返回的result可用时,从CM得到通知。
一组类似的event驱动调用(有返回值了就返回,之前都block):get(),wait(),then().
get(): 获取返回值或者异常
wait():有 等待直到获得结构、等待一段事件就返回 两种机制
then(): 注册一个收到result之后的callback函数。
后面两个好像是封装的std::future的机制.
如果不想事件驱动,而要轮询,可以轮询ara::Future::is_ready()。这个是非阻塞的。如果返回了TRUE。就说明现在是有结果的。然后调用get()。直接抓取返回值就好。肯定不会阻塞。
当我们调用了Method在等待结果的过程中不想要结果了,要取消调用,可以调用ara::core::Future的dtor。告诉CM不要再调用result返回时的回调函数了,释放预留的memory资源,停止事件等待机制。
要调用析构函数,我们可以显示地让future “go out of scope”。我觉得可能是设置为局部的对象,然后随着整个局部域一起释放。
如果架构不支持,可以实例化一个新的默认future赋值给之前的future实例。来强制调用正在等待Menthod反馈的future实例的析构器。
Fields
proxy::fields::
操作和events一样,就是每次给的数据(value)都是固定的初始值。client可以通过Get()取得这个值,也可以通过Set()更新这个值。
可以这么说,fields可以看作是拥有set 和 get 这两个method 的event。service可以通过CM来通知proxy field发生。proxy可以调用Get和Set这两个Method…