VSomeip的routing_manager分析

最近和vsomeip杠上了,主要还是工作中的项目需求,在vsomeip中有一个非常重要的组件叫routingmanager,本文主要分析以下routing_manager的工作流程。

routing_manager的作用:

首先需要明白一点,vsomeip是一个提供IPC和RPC(局域网)的中间层框架,在通信的两端,以提供服务和请求服务的方式来作为两端通信的基础,用于注册服务端可以提供的服务,在注册时routing manager将会记录下server端的endpoint,由于vsomeip是基于socket的,所以通俗的讲,routing manager将会记录下server端的ip 和 port,以及client id。 client ID是每一个vsomeip应用都会被指定的一个唯一ID号,可以由应用自行指定,配置在vsomeip.json中,也可以由vsomeip顺序分配:

上图是vsomeip的片段,该片段定义了在本机上有两个vsomeip应用,分别指定了两个应用的名称和id。在后续使用vsomeip时需要runtime::get()->create_application("serversample");注意,在调用create_application时需要将application的名字传入,这个名字和进程的名字没有任何关系,但是需要和json中配置的名称相同。在创建application的同时,vsomeip会通过configuration模块查找application的id,而这个id就是client_id。

而在vsomeip的配置文件中,需要指定在本机上由哪个应用来充当routing_manager的角色:

通过上面这句,指定server_sample作为本机上vsomeip的routing_manager,而routing_manager的基本功能如下图所示,主要是提供server端对可以提供的service的注册以及提供client端对service的查询,从而建立起两端的链接:

基本的步骤为:

1, 系统需要选定routing_manager的应用为哪一个,作为routing_manager的应用将会承担起应用工作本身之外的工作,对于作为routing_manager的应用,系统在初始化routing组件的时候将会创建一个routing_manager_impl对象,该类封装了routing_manager服务的管理功能,将会创建一个固定port的socket,port的值由以下宏定义在internal.hpp中:

#define VSOMEIP_INTERNAL_BASE_PORT 51234

创建socket的工作将被封装在一个routing_manager_stub中,在stub调用init()函数是通过init_routing_endpoint函数来创建的:

vsomeip对socket的接口做了一些封装,主要使用了boost::asio库中的网络接口来实现,前面说了,这里重申一次,有一个问题必须要明白,routing_manager提供的是本机上应用的service查询和注册的功能,也就是说在使用routing_manager的时候仅仅提供一个service的通信方式查询或者记录,所以并不需要提供一个远端的socket接口,换句话说这个socket是基于127.0.0.1这个环回地址的。对于远端服务的发现则是通过service_discovery组件来实现的,service_discovery使用组播socket来实现,但是service_discovery对于接收到的service注册信息,依旧是在routing_manager中来保存的。在远端通信中,每一台参与通信的机器都需要指定一个本地的routing_manager,当然如果没有指定的话,系统将会选用第一个启动的app作为routing_manager。关于service_discovery的事情,我们以后在讨论。基于上述的内容,routing_manager创建了一个在51234端口的socket(仅限于windows,在linux上,vsomeip将会创建一个UDS 来作为routing_manager的通信端),vsomeip为本地socket的server端专门编写了一个封装类:local_server_endpoint_impl,该类主要封装了boost中的io_service和tcp socket来实现socket的accept和receive,那么有来有往,除了收信息之外当然还要发送信息,可能听起来有些奇怪,作为routing_manager要发送什么呢?首先,我们前面说过,routing_manager不是一个独立专门的应用程序,而是一个普通的vsomeip应用,只是在完成自己的工作之余,需要承担routing_manager的职责,所以作为一个普通的vsomeip当然有申请其他服务的权利,所以当需要发送message给其他service的时候则需要send接口。

2, 注册服务

注册服务分为两个方面,首先需要在配置的json文件中将要提供的service信息填写好,这些信息实际上并不会直接注册服务,只是将服务的一些信息记录在configuration模块中,以便使用时做查询,比如说上例中会记录service id和instance id,这两个信息是最重要的,用以唯一标记一个服务,而其他的信息则是表明service的附加信息,例如reliable接口表明该service的socket是基于tcp的,如果是unreliable则是基于udp,port定义了socket端口号,events定义了可以被监听的事件,eventgroup则定义了事件组,在vsomeip中想要监听一个事件,需要监听这个事件所在的组。

在json中定义一个service仅仅是注册服务的第一步,第二步则是server端调用offer_service接口,向routing_manager注册服务。这里将会被分为两种情况,首先,如果server端本身就是routing_manager的主体,则注册的事件将会被直接处理,注册的service将被记录在routing_manager_base的local_services_中:

需要注意的是,这里的local_services_仅仅记录了service的最基本信息:service ID, instance ID, major_version, minor_version以及提供service应用的client ID。而endpoint,也就是用于通信的socket封装接口,则是保存在server_endpoints_中的:

这里所指的local service并非本应用所提供的的service,而是本机所提供的service。

从server_endpoints_t的定义可以看出,当需要和一个服务通信时,首先通过local_services_找到对应的client ID,之后通过ID来查找server的endpoint。这里的map是嵌套结构,内嵌的map key值是is_reliable,也就是说tcp的端口和udp的端口是分开定义的。

在这个过程中需要关注的过程主要是configuration_impl中对于services的加载部分,已经routing_manager_impl的init和start函数。

从上面的片段我们可以看到,当server端调用offer_service时,首先调用handle_local_offer_service,这个函数主要有两个工作,第一个创建serviceinfo (见serviceinfo.hpp), 尽管从名称上看serviceinfo是一个非常重要的service信息,然而其中所记录的信息并不全面,甚至所包含的endpoint成员也并未被真正使用,而server的endpiont信息则是记录在server_endpoints_表中的,所以当需要给一个server端发送信息时,首先通过service的ID和instance来查找到service的client ID,再通过client ID来找到server端的endpoint,通过这个endpoint便可以想server端发送信息。handle_local_offer_service的第二个工作就是将service的信息在local_services_中登记,前面说了通信时需要通过service ID和instance ID来查找client ID,而查找的方式就是通过local_services_。

在handle_local_offer_service之后则进入了init_service_info函数,而这个函数中最重要的便是创建endpoint,关于endpoint,我们需要明白的是之所以会调用offer_service, 就表明这个application是server端,所以这里创建的endpoint是server端用来接收消息,创建的endpoint是一个receiver形式,在vsomeip中被封装成了tcp_server_endpoint_impl:

之所以没有使用local_server_endpoint_impl是因为提供的service可能会被远端调用,而前面routing_manager之所以会创建local_server_endpoint_impl是由于routing_manager仅对本机的vsomeip提供服务。既然要创建tcp的endpoint,那么就必须指定一个ip和port,这里的ip由json文件中的unicast来指定,而port由json中的reliable或unreliable来指定:

如果只想提供一个本地的service,那么unicast就填成环回地址127.0.0.1,对于remote的service,在server端的json配置文件中需要填写成server端的ip地址。另外,如果提供remote的service,需要enable service_discovery服务,在json中配置就可以了:

需要说明的是,service_discovery使用一个组播地址,所以multicast配置为你要使用的组播地址即可,在此必须要确认的是你的路由器能否支持组播。

对于第二种情况,如果提供service的application并非routing_manager,那么在创建routing_manager接口时,将会创建routing_mananger_proxy而不是routing_manager_impl:

在创建之后同样调用routing_mananger_proxy的init函数:

在这里,sender是一个local_client_endpoint_impl,其封装了在环回地址上发送端的socket,其端口是VSOMEIP_INTERNAL_BASE_PORT,刚好和之前的routing_manager_impl中的server对应起来,那么这里出现了一个新的问题,VSOMEIP_ROUTING_CLIENT又是什么?

从定义上看这只是一个数字,这个数字的作用是用来区分routing_manager_stub端所收到的消息是否是一个routing的消息,从之前的内容可知,routing_manager的host并非是一个单独的应用,而是内嵌到一个普通的someip应用中的,那么这里便存在一个问题,如何区分一个消息是发送给这个应用本身还是发送给routing_manager的,如果是发送给routing_manager则在消息的client值上填写VSOMEIP_ROUTING_CLIENT,否则填写应用的真实ID:

这里的问题是,在创建用于routing_manager的endpoint是是在init_routing_endpoint中,使用了VSOMEIP_INTERNAL_BASE_PORT作为port,而创建应用本身所需的endpoint时是在create_local_receiver,使用了VSOMEIP_INTERNAL_BASE_PORT+host_->get_client(),道理上不会冲突,但创建两处endpoint时,其使用了相同的回调函数,具体在local_server_endpoint_impl.cpp的510行:

这里的its_host是routing_manager_stub,而routing_manager_stub的on_message函数,又会转调routing_manager_impl的on_message函数:

继而,在routing_manager_impl的on_message函数对client id加以区分。

前面说过,如果server端并非routing_manager的host,那么创建的便是routing_manager_proxy类,前面的一大堆讲到proxy类创建了sender成员,那么这个sender就是用来对routing_manager_stub发送routing请求的。所以后续在server的offer_service函数都将间接转发到stub端来处理。从前面的routing_manager_proxy::init函数中可知,proxy同样会创建一个用来接收消息的socket,这是通过init_receiver函数来实现的,至此,我们发现每一个application都会创建至少一个server endpoint用以接收其他application发来的请求,在创建时,port被指定为VSOMEIP_INTERNAL_BASE_PORT+client_,而这个client_便是client所指定的id:

所以我们可以大胆猜测,在request_service时,同样需要使用对应的port来进行服务通信,所以当request_service在查找服务时所要查找的便是提供service的server所用的client id,便可以得到该port。而对于remote service则有另一套查找方式。

在proxy端,request_service需要将请求的service信息发送到stub端:

而在stub端,收到请求后将通过find_local函数来查找client,继而将request信息发送出去,对于普通的send message而言,proxy会首先查找本地的服务,见routing_manager_proxy::send,如果查找不到,则发送信息给routing_manager_stub处理:

至此,我觉得routing_manager的内容,我已经讲清楚了,最后补一张类图(我的UML基本就是自己能看懂的水平):

VSomeip的routing_manager分析 - 知乎 (zhihu.com)icon-default.png?t=N6B9https://zhuanlan.zhihu.com/p/452835290

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值