编辑推荐: |
本文主要介绍了面向服务架构(SOA)相关内容。 希望对您的学习有所帮助。
本文来自于阿里云,由火龙果软件Linda编辑、推荐。 |
|
1 面向服务架构(SOA)的概述及意义
1.1 面向服务架构概述
开局一张图,先有个大概的印象。服务的设计一般包括图中的几个部分:
• 软件组件的设计
• 软件组件的服务接口的设计(详细可进一步为方法和事件及属性的设计)
一般传统的架构设计方法是:系统被划分为子系统,各个子系统通过定义的接口,实现交互通信,一般子系统之间的依赖性较高。而面向服务的体系架构的设计方法是:不同的系统资源被打包到一个“服务”中,该“服务”提供特定的系统功能,同时保持它们自己的内部状态。实现服务的组件代表服务的单个实例,其由服务实例ID标识。当客户端想要使用服务实例时,它只需要遵循定义语言规范来请求服务。我们先看一下规范怎么定义服务和服务接口及服务实例的?
服务的接口以标准定义语言指定,该语言将在系统的每个元素之间共享。其包含三个要素:方法,事件和属性(也叫Filed)。
我们先看一下规范怎么定义方法,事件和属性的?
注意:
1 、事件和属性的区别就是属性是具有记忆性和初始状态,而事件就像快照一样,没有历史状态。
2、同一服务的多个实例可能存在于单个系统中。在这些情况下,将需要一个服务发现和实例选择过程。虽然可以让客户端处理这个过程,但它会增加系统的复杂性。因此,考虑到
SOA 开发的技术已经提供了这种“服务聚合器”。聚合器执行双重角色。它充当服务客户端的服务提供者,也充当实际不同服务提供者的服务客户端。使用哪个服务实例的选择由请求者决定。
1.2 面向服务架构(SOA)的意义
• 1 、面向服务架构确保松散耦合的软件模块,因为每个服务只保存执行其逻辑所需的信息,并且无论系统作为一个整体的状态如何,只要请求遵循定义语言规范,它就会继续执行其功能。由于这个事实,SOA
与平台和语言无关。只要请求或响应遵循规范,客户端或服务都没有必要深入了解彼此的实现细节。因此,可以无缝集成可能由不同供应商设计的异构组件。
• 2、此外,SOA 不需要静态系统配置。通过使用服务发现,服务提供者可以在运行时被发现。这实际上意味着可以将组件“热插拔”到系统中。不再需要关闭整个系统来更新或添加新组件。
2 服务(SOME/IP)的通信模式
在SOME/IP 通信过程中,服务端和客户端之间的通信方式主要为Events 和Methods方式,其分类如下图所示:
SOME/IP 通信过程中,服务端和客户端之间交互的序列图为:
下面详细介绍每一种通信模式。
2.1 Request/Response Methods
2.1.1 RR流程概述
最常见的通信模式之一是请求/响应模式。一个通信伙伴(客户端)发送请求消息,由另一个通信伙伴(服务器)应答。客户端会根据需要请求的服务内容构建请求报文的报头和有效载荷,具体如下:
• 构建有效载荷
• 根据客户端要调用的方法设置Message ID
• 将 Length 字段设置为 8 字节(对于 SOME/IP 标头的长度字段之后的部分)+ 序列化有效负载的长度
• 可选择将请求 ID 设置为唯一编号(仅对客户端唯一)
• 设置协议版本
• 根据接口定义设置接口版本
• 将消息类型设置为请求(即0x00)
• 将返回码设置为0x00
服务器根据收到的请求报文的内容,执行相应的服务之后发送响应报 文,具体如下:
• 构建有效载荷
• 从对应的请求中提取Message ID,复制到响应报文中
• 将长度设置为 8 字节 + 新的有效负载大小
• 从对应的请求中提取Request ID,复制到响应报文中
• 将消息类型设置为RESPONSE(即0x80)或ERROR(即0x81)
• 将 Return Code 设置为被调用方法的返回代码,或者在错误消息的情况下设置为有效的错误代码,例如下表所示:

2.1.2 RR代码解析
2.1.2.1 服务和客户端的配置
//Someip_Cfg.c
static Someip_ServiceType Someip_Services[] = {
{
.Id = 0x1111,
.InstanceId = 0x2222,
.MethodId = 0x3333,
.MajorVersion = 0,
.MinorVersion = 0,
.EventId = 0x8778,
},
};
const Someip_InstanceType Someip_Instance = {
.NoOfServices = 1,
.Service = Someip_Services,
};
//someip.c
static uint16_t MethodId = 0x3333;
static uint16_t ClientId = 0x4444;
|
2.1.2.2 服务和客户端的注册
//someip.c
//注册客户端
someip_app_t *someip_register_app(client_t my_id)
{
someip_app_t *srv;
srv = (someip_app_t *)malloc(sizeof(someip_app_t));
if(!srv) {
return NULL;
}
srv->client_id = my_id;
srv->req_id = 0;
return srv;
}
//注册服务(请求)
someip_req_t request_service(someip_app_t *app, service_t service_id, instance_t instance, method_t method,
void (*avail_handler)(service_t service, instance_t instance, int available) )
{
someip_requested_service_t *srv;
srv = (someip_requested_service_t *)malloc(sizeof(someip_requested_service_t));
if(!srv) {
return NULL;
}
srv->req = app;
srv->service_id = service_id;
srv->instance = instance;
srv->avail_handler = avail_handler;
someip_add_req_service(srv);
return (someip_req_t)srv;
}
//读取配置实现注册的对象的实例化
void Someip_Init()
{
int i;
printf("[Someip] Someip_Init \n");
app = someip_register_app(ClientId);
for(i=0; i < Someip_Instance.NoOfServices; i++)
{
service_t service_id = (service_t)Someip_Instance.Service[i].Id;
instance_t instance = (instance_t)Someip_Instance.Service[i].InstanceId;
request_service(app, service_id, instance, MethodId, sample_avail_handler);
}
}
|
2.1.2.3 RR请求响应服务
void Someip_SendRequest(someip_requested_service_t *service)
{
// 创建临时缓存buf[256],往里填充someip请求报文数据
uint8 buf[256];
// data指向数组的buf头,并把这一段内存类型转换成someip_t类型
someip_t *data = (someip_t *)buf;
//填充someip报文头,注意消息类型为0x0:REQUEST(期望得到响应的请求)
data->length = htonl(0xd);
data->msg_id = htonl(MAKE_ID(service->service_id, service->method));
data->req_id = htonl(MAKE_ID(service->req->client_id, service->req->req_id));
data->protocol_ver = 0x1;
data->interface_ver = 0x0;
data->msg_type = 0x0;
data->ret_code = 0x0;
//填充someip有效载荷Payload
strcpy(data->payload, "world");
//序列化:内存类型转换成为uint8的pduinfo.SduDataPtr
PduInfoType pduInfo;
pduInfo.SduDataPtr = (uint8 *)data;
pduInfo.SduLength = 8 + ntohl(data->length);
//调用底层发送函数,参数2时tx_socket的句柄
Someip_SendPacket(&pduInfo, 2);
}
void Someip_SendPacket(PduInfoType *pduInfo, SoAd_SoConIdType tx_socket)
{
TcpIp_SockAddrType destination;
uint8 netmask;
TcpIp_SockAddrType default_router;
destination.domain = TCPIP_AF_INET;
default_router.domain = TCPIP_AF_INET;
/* Set the remote multicast address before sending */
//ip地址为:10.10.0.22,端口号为SoAd_GetLastPort的返回值
destination.addr[0] = 10;
destination.addr[1] = 10;
destination.addr[2] = 0;
destination.addr[3] = 22;
destination.port = htons(SoAd_GetLastPort());
printf("[Someip] Send Packet %d\n", htons(SoAd_GetLastPort()));
//tx_socket和远程地址进行绑定
(void)SoAd_SetRemoteAddr(tx_socket, &destination);
const TcpIp_SockAddrType wildcard = {
(TcpIp_DomainType) TCPIP_AF_INET,
TCPIP_PORT_ANY,
{TCPIP_IPADDR_ANY, TCPIP_IPADDR_ANY, TCPIP_IPADDR_ANY, TCPIP_IPADDR_ANY }
};
//调用底层发送函数
SoAd_IfTransmit(tx_socket, pduInfo);
//tx_socket和远程地址进行解绑
(void)SoAd_SetRemoteAddr(tx_socket, &wildcard);
}
void Someip_SendResponse(someip_requested_service_t *service, uint8 *payload, uint32 length)
{
uint8 buf[256];
someip_t *data = (someip_t *)buf;
data->length = htonl(8 + length);//lh htonl就是把本机字节顺序转化为网络字节顺序
data->msg_id = htonl(MAKE_ID(service->service_id, service->method));
data->req_id = htonl(MAKE_ID(service->req->client_id, service->req->req_id));
data->protocol_ver = 0x1;
data->interface_ver = 0x0;
//将消息类型设置为RESPONSE(即0x80)
data->msg_type = 0x80;
data->ret_code = 0x0;
PduInfoType pduInfo;
pduInfo.SduDataPtr = (uint8 *)data;
pduInfo.SduLength = 8 + ntohl(data->length);
strncpy(data->payload, payload, length);
Someip_SendPacket(&pduInfo, 1);
}
//接收到Someip报文的回调函数
void Someip_RxIndication(PduIdType RxPduId, const PduInfoType *PduData)
{
uint8 *data = PduData->SduDataPtr;
//创建someip_t类型变量SomeipPtr对接收到的数据进行解析
someip_t *SomeipPtr = (someip_t *)data;
int i;
uint32 RequestId, NotifyId;
printf("[Someip] Someip_RxIndication %d\n", RxPduId);
for(i=0; i < Someip_Instance.NoOfServices; i++)
{
//抽取配置的服务ID和服务实例ID,方法ID/事件ID
service_t id = Someip_Instance.Service[i].Id;
instance_t instance = Someip_Instance.Service[i].InstanceId;
method_t method = Someip_Instance.Service[i].MethodId;
uint16_t event = Someip_Instance.Service[i].EventId;
//得到someip的消息 ID(NotifyId/RequestId)
NotifyId = MAKE_ID(id, event);
RequestId = MAKE_ID(id, method);
printf("%x %x %x\n", SomeipPtr->msg_id, NotifyId, RequestId);
if(ntohl(SomeipPtr->msg_id) == NotifyId)
{
printf("[Someip] Get Notification\n");
someip_requested_service_t *service = someip_find_req_service(id, ClientId, instance);
if(service != NULL)
service->avail_handler(service);
Someip_SendRequest(service);
}
else if(ntohl(SomeipPtr->msg_id) == RequestId)
{
printf("[Someip] Get Request\n");
//ClientId:0x4444,要先寻找服务
someip_requested_service_t *service = someip_find_req_service(id, ClientId, instance);
if(service != NULL)
service->avail_handler(service);
Someip_SendResponse(service, SomeipPtr->payload, ntohl(SomeipPtr->length) - 8);
}
else
{
printf("[Someip] Unknown Services\n");
}
}
}
|
|