面向服务架构(SOA)吐血整理

简介: 面向服务架构(SOA)吐血整理

1 面向服务架构(SOA)的概述及意义

1.1 面向服务架构概述

开局一张图,先有个大概的印象。服务的设计一般包括图中的几个部分:

  • 软件组件的设计
  • 软件组件的服务接口的设计(详细可进一步为方法和事件及属性的设计)
  • 61b5e4b4db7eb1c04fbf307829d74513_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

一般传统的架构设计方法是:系统被划分为子系统,各个子系统通过定义的接口,实现交互通信,一般子系统之间的依赖性较高。而面向服务的体系架构的设计方法是:不同的系统资源被打包到一个“服务”中,该“服务”提供特定的系统功能,同时保持它们自己的内部状态。实现服务的组件代表服务的单个实例,其由服务实例ID标识。当客户端想要使用服务实例时,它只需要遵循定义语言规范来请求服务。我们先看一下规范怎么定义服务和服务接口及服务实例的?

缩写/首字母缩略词: 描述:
Service 零个或多个方法methods、零个或多个事件events以及零个或多个字段fields的逻辑组合(允许空服务,例如用于在 SOME/IP-SD 中声明非 SOME/IP 服务)。说人话就是一个离散功能单元,我们可以封装成一个函数来实现这个功能
Service Interface 服务「包括其方法,事件和字段」的正式规范(formal specification ),说人话就是能够被其他模块调用的函数名称/API ,服务通过这个函数名称/API被其他ECU所使用
Service Instance 服务接口的软件实现,可以在车辆上或ECU 上存在不止一次 ,说人话就是一个函数名称/API的定义和实现

服务的接口以标准定义语言指定,该语言将在系统的每个元素之间共享。其包含三个要素:方法,事件和属性(也叫Filed)。

我们先看一下规范怎么定义方法,事件和属性的?

缩写/首字母缩略词: 描述:
Method 方法、过程、函数或被调用的子例程。(即从客户端到服务的消息),根据服务器是否有反馈结果分为请求/响应(Request/Response, R/R)通信和Fire&Forget(F&F)通信
Event 一种单向数据传输,根据实际的应用场景,可以有不同的发送方式。即仅在数据改变时调用或循环调用,并从数据的producer生产者发送到consumers消费者(即从服务端到客户端的消息)
Field Field,有三种属性:getter、setter 和 notfier。它用以表示某一功能的状态量,可以通过Method发布控制命令,即Setter;也可以通过Method去请求获取状态,即Getter;在状态发生改变时也可以发送通知,即Notification(同Event)
Notification Event 字段通知器(the notifier of an field) 发送的事件消息。此类通知者的消息无法与事件消息区分开来;因此,当提到( refering to )一个事件的消息时,这应该也适用于字段通知者的消息。
Getter 允许对字段Field进行读取访问的请求/响应调用。
Setter 允许对字段Field进行写访问的请求/响应调用。
Notifier Notifier在消息字段Field的值更改时发送具有新值的事件消息




注意:

1 、事件和属性的区别就是属性是具有记忆性和初始状态,而事件就像快照一样,没有历史状态。

2、同一服务的多个实例可能存在于单个系统中。在这些情况下,将需要一个服务发现和实例选择过程。虽然可以让客户端处理这个过程,但它会增加系统的复杂性。因此,考虑到 SOA 开发的技术已经提供了这种“服务聚合器”。聚合器执行双重角色。它充当服务客户端的服务提供者,也充当实际不同服务提供者的服务客户端。使用哪个服务实例的选择由请求者决定。

1.2 面向服务架构(SOA)的意义

  • 1 、面向服务架构确保松散耦合的软件模块,因为每个服务只保存执行其逻辑所需的信息,并且无论系统作为一个整体的状态如何,只要请求遵循定义语言规范,它就会继续执行其功能。由于这个事实,SOA 与平台和语言无关。只要请求或响应遵循规范,客户端或服务都没有必要深入了解彼此的实现细节。因此,可以无缝集成可能由不同供应商设计的异构组件。
  • 2、此外,SOA 不需要静态系统配置。通过使用服务发现,服务提供者可以在运行时被发现。这实际上意味着可以将组件“热插拔”到系统中。不再需要关闭整个系统来更新或添加新组件。

2 服务(SOME/IP)的通信模式

在SOME/IP 通信过程中,服务端和客户端之间的通信方式主要为Events 和Methods方式,其分类如下图所示:

a417ee47e1b2ce7ac50f97e35a7a2068_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

SOME/IP 通信过程中,服务端和客户端之间交互的序列图为:

90b5e5542d801c2204f52f949682bff5_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png

下面详细介绍每一种通信模式。

2.1 Request/Response Methods

2.1.1 RR流程概述

683e1c65f763aef90eacf7320a735350_640_wx_fmt=png&wxfrom=5&wx_lazy=1&wx_co=1.png最常见的通信模式之一是请求/响应模式。一个通信伙伴(客户端)发送请求消息,由另一个通信伙伴(服务器)应答。客户端会根据需要请求的服务内容构建请求报文的报头和有效载荷,具体如下:

  • 构建有效载荷
  • 根据客户端要调用的方法设置Message ID
  • 将 Length 字段设置为 8 字节(对于 SOME/IP 标头的长度字段之后的部分)+ 序列化有效负载的长度
  • 可选择将请求 ID 设置为唯一编号(仅对客户端唯一)
  • 设置协议版本
  • 根据接口定义设置接口版本
  • 将消息类型设置为请求(即0x00)
  • 将返回码设置为0x00
Number Value Description
0x00 REQUEST 期望得到响应的请求
0x01 REQUEST_NO_RETURN 不期望得到响应的请求
0x02 NOTIFICATION 不期望得到响应的请求的通知/事件回调
0x80 RESPONSE 响应消息
0x81 ERROR 响应消息包含错误

服务器根据收到的请求报文的内容,执行相应的服务之后发送响应报 文,具体如下:

  • 构建有效载荷
  • 从对应的请求中提取Message ID,复制到响应报文中
  • 将长度设置为 8 字节 + 新的有效负载大小
  • 从对应的请求中提取Request ID,复制到响应报文中
  • 将消息类型设置为RESPONSE(即0x80)或ERROR(即0x81)
  • 将 Return Code 设置为被调用方法的返回代码,或者在错误消息的情况下设置为有效的错误代码,例如下表所示:
ID 名称 描述
0x00 E_OK 没有发生错误
0x01 E_NOT_OK 发生未指定的错误
0x06 E_TIMEOUT 发生超时(仅限内部错误代码)。

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");
  }
 }
}
相关文章
|
6月前
|
缓存 监控 数据格式
信息系统架构模型(2) SOA
信息系统架构模型(2) SOA
141 0
|
3月前
|
Cloud Native 云计算 微服务
云原生时代:企业分布式应用架构的惊人蜕变,从SOA到微服务的大逃亡!
【8月更文挑战第8天】在云计算与容器技术推动下,企业分布式应用架构正经历从SOA到微服务再到云原生的深刻变革。SOA强调服务重用与组合,通过标准化接口实现服务解耦;微服务以细粒度划分服务,增强系统灵活性;云原生架构借助容器化与自动化技术简化部署与管理。每一步演进都为企业带来新的技术挑战与机遇。
124 6
|
5月前
|
边缘计算 Cloud Native
“论SOA在企业集成架构设计中的应用”必过范文,突击2024软考高项论文
SOA架构,即面向服务的架构,它将系统中的所有功能都拆分为一个个独立的服务单元。这些服务通过相互间的沟通与配合,共同完成了整体业务逻辑的运作。在SOA架构中有几个核心概念:服务提供者、服务使用者、服务注册中心、服务规范、服务合同,这些概念清晰地阐述了服务应如何被提
230 6
“论SOA在企业集成架构设计中的应用”必过范文,突击2024软考高项论文
|
4月前
|
消息中间件 安全 NoSQL
「架构」SOA(面向服务的架构)
**SOA**是构建灵活企业IT系统的架构模式,基于服务组件进行设计。它强调服务的自包含、模块化,通过服务识别、抽象、组合和交互实现业务流程。特点包括松耦合、重用性、互操作性和标准化。优点是灵活性、可维护性、可扩展性和成本效益,但也有复杂性、性能和治理问题。设计策略涉及业务能力识别、服务契约定义和服务目录建立。技术栈涵盖Java EE、.NET、SOAP、REST、服务治理工具和各种数据库、消息队列及安全标准。SOA旨在适应变化,但也需妥善管理和规划。
178 0
|
4月前
|
Kubernetes API 微服务
「架构风格」SOA(面向服务)和微服务
**SOA与微服务对比摘要**: - **SOA**:企业级,服务粒度大,重用性强,常通过ESB通信,服务部署集中,技术栈统一。 - **微服务**:服务粒度小,单一职责,轻量级协议如REST,独立部署,技术多样性,去中心化治理。 - **区别**:服务大小、独立性、通信协议、部署方式和技术栈不同,微服务更强调敏捷和独立性。 - **示例**:Python Flask简单示例展示了服务创建,SOA服务间通过HTTP请求通信,微服务每个服务独立运行。 - **权衡**:涉及服务发现、负载均衡、容错和安全,常用技术如Docker、Kubernetes和API网关。
414 0
|
5月前
|
边缘计算 Cloud Native IDE
“论SOA在企业集成架构设计中的应用”写作框架,系统架构设计师
企业应用集成(Enterprise Application Integration, EAI)是每个企业都必须要面对的实际问题。面向服务的企业应用集成是一种基于面向服务体系结构(Service-OrientedArchitecture,SOA)的新型企业应用集成技术,强调将企业和组织内部的资源和业务功能暴露为服务,实现资源共享和系统之间的互操作性,并支持快速地将新的应用以服务的形式加入到已有的集成环境中,增强企业IT环境的灵活性。
120 0
|
6月前
|
消息中间件 Kubernetes 供应链
软件体系结构 - 架构风格(14)SOA架构风格
【4月更文挑战第21天】软件体系结构 - 架构风格(14)SOA架构风格
159 0
|
11月前
|
前端开发 Java 应用服务中间件
单体架构、垂直应用架构、分布式、SOA、微服务之间有什么关系和区别
单体架构、垂直应用架构、分布式、SOA、微服务之间有什么关系和区别
289 5
|
Dubbo Java 应用服务中间件
09分布式电商项目 - SOA架构演变
09分布式电商项目 - SOA架构演变
128 0
|
SQL Java Nacos
SpringCloud+SpringCloudAlibaba+SOA架构搭建,使用nacos注册中心,gateway网关配置
SpringCloud+SpringCloudAlibaba+SOA架构搭建,使用nacos注册中心,gateway网关配置
230 0