您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   模型库  
会员   
   
人工智能、机器学习 TensorFlow
6月30日-7月1日 直播
基于 UML 和EA进行分析设计
7月30-31日 北京+线上
图数据库与知识图谱
8月21日-22日 北京+线上
   
 
 订阅
面向服务架构(SOA)吐血整理
 
 
  101  次浏览      6 次
 2025-6-24
 
编辑推荐:
本文主要介绍了面向服务架构(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");
  }
 }
}
   
101 次浏览       6
 
相关文章

CMM之后对CMMI的思考
对软件研发项目管理的深入探讨
软件过程改进
软件过程改进的实现
 
相关文档

软件过程改进框架
软件过程改进的CMM-TSP-PSP模型
过程塑造(小型软件团队过程改进)
软件过程改进:经验和教训
 
相关课程

以"我"为中心的过程改进(iProcess )
iProcess过程改进实践
CMMI体系与实践
基于CMMI标准的软件质量保证

最新活动计划
人工智能.机器学习TensorFlow 6-30[直播]
基于 UML 和EA进行分析设计 7-30[北京]
软件架构设计方法、案例与实践 7-24[北京]
用户体验、易用性测试与评估 7-25[西安]
图数据库与知识图谱 8-23[北京]
需求分析师能力培养 8-28[北京]
 
 
最新文章
iPerson的过程观:要 过程 or 结果
基于模型的需求管理方法与工具
敏捷产品管理之 Story
敏捷开发需求管理(产品backlog)
Kanban看板管理实践精要
最新课程
基于iProcess的敏捷过程
软件开发过程中的项目管理
持续集成与敏捷开发
敏捷过程实践
敏捷测试-简单而可行
更多...   
成功案例
英特尔 SCRUM-敏捷开发实战
某著名汽车 敏捷开发过程与管理实践
北京 敏捷开发过程与项目管理
东方证券 基于看板的敏捷方法实践
亚信 工作量估算
更多...