18、基于SOA架构的系统构建与模式应用

基于SOA架构的系统构建与模式应用

1. 系统需求与SOA架构选择

系统的需求随着时间不断演变。最初,系统仅需处理3G视频通话中的识别以及少量链接。随后,业务增加了对短信和电子邮件的需求,接着需要处理大量链接,并为移动应用和通用互联网使用开放平台。

为了满足这些需求,我们决定基于面向服务架构(SOA)原则构建系统。SOA的灵活性使我们能够添加更多组件(服务),并在保持系统运行的同时发展现有服务的内部结构,从而不断适应不断变化的需求。

2. 系统中的服务

系统包含多种服务,这些服务将用于展示各种模式的实现方式:
- 适配服务 :Email Gateway、MMS Gateway和Phone Client将外部协议转换为内部协议。
- 3G视频通话服务 :SIP Listener、RTP Listener、Player、Call Recovery和Third - Party (3G Video Call) Gateway处理3G视频通话的不同方面。
- B2B服务 :Billing处理计费,Interactions处理用户交互,Campaigns处理广告活动。
- 技术服务 :Liveliness Monitor、Monitor和Watchdog负责保持系统运行。
- 数据收集服务 :Statistics Collector和Reporting负责收集系统中的信息并生成报告。
- 图像识别服务 :Identification和ID Workers构成图像识别子系统。

以下是业务服务与实现它们的SOA服务的映射关系:
| 业务服务 | 涉及的服务 |
| — | — |
| App图像搜索 | Phone Client, Identification, Identification Workers, Watchdog, Billing, Statistics Collector, Campaigns |
| Email图像搜索 | Email Gateway, Identification, Identification Workers, Watchdog, Billing, Statistics Collector, Campaigns |
| MMS搜索 | MMS Gateway, Identification, Identification Workers, Watchdog, Billing, Statistics Collector, Campaigns |
| 3G视频通话搜索 | SIP Listener, RTP Listener, Player, Call Recovery, Identification, Identification Workers, Watchdog, Billing, Statistics Collector, Campaigns |
| 计费 | Billing, Reporting |
| 活动管理 | Campaigns, Reporting |
| 交互 | Interactions, Reporting, Campaigns |

3. 系统结构模式
3.1 Edge Component模式

Edge Component模式是一种基本的架构模式,可在多个层面应用。在架构层面,不同的图像发送方式(智能手机应用、MMS和3G视频通话)与实际执行识别和搜索的业务逻辑分离,不同的网关作为边缘组件。

例如,Email Gateway使用IMAP协议获取电子邮件,提取其中的图像,并调用Identification服务进行搜索。

同时,从服务的定义来看,不同的网关可以被视为服务。以Email Gateway为例:
- 粗粒度和自治性 :该组件独立运行,能处理与电子邮件相关的所有事务,并与其他服务通信以提供业务功能。
- 使用合同和可发现地址的消息 :合同基于已知协议(IMAP),消息结构为必须带有支持格式图像附件的电子邮件,且邮件发送到特定邮箱(端点)。
- 受策略管理 :可设置的策略包括接受的电子邮件来源、发件人地址的可验证性等。

在代码层面,我们也实现了Edge Component模式。以下是发送MMS消息的简单WCF合同:

[ServiceContract]  
public interface IHandleSendMms  
{ 
    [OperationContract] 
    int SendMms(SendMmsRequest eventOccurred);  
}  

[DataContract]  
public class SendMmsRequest : ImEvent  
{ 
    /// <summary> 
    /// end user's number. should be in international  
    ///  format: +[country-code]number. Example: +491737692260
    /// </summary>
    [DataMember]
      public string ToNumber { get; set; } 
    /// <summary> 
    /// service's number, usually a short-code. Example: 84343 
    /// </summary> 
    [DataMember] 
    public string FromNumber { get; set; } 
    /// <summary> 
    /// Text, as byte array. Use Encoding classes to do it. 
    /// </summary> 
    [DataMember] 
    public byte[] TextAsByteArray { get; set; } 
    /// <summary> 
    /// Image, as byte array. Can be: jpg, gif, png, bmp. (jpg rulez!!) 
    /// </summary> 
   [DataMember] 
    public string ImageExtension { get; set; } 
    /// <summary> 
    /// the mms message should have a subject. just put something there. 
    /// </summary> 
    [DataMember] 
    public string Subject { get; set; }  
}  

以下是边缘组件中实现该合同的方法:

public int SendMms(SendMmsRequest eventOccurred) 
 {  
var eventContext = eventOccurred.ToString();  
if (log.IsDebugEnabled) 
    log.Debug("inside 'SendMms', event context = ["  
➥ + eventContext + "]");  
var fromNumber = eventOccurred.FromNumber; 
var sender = mmsSenderFactory.Get(fromNumber); 
if (null == sender) 
{
    if (log.IsWarnEnabled)
        log.Warn("cannot get mms sender derived from '" 
➥+ (fromNumber ?? "null") + "'");
 return 0;  
}  
IMmsSubmitResponse response;  
try  
{ 
    var extension= GetImageExtension  
➥(eventOccurred.ImageAsByteArray);
    var mmsMessageDetails = new MmsMessageDetails 
➥           (eventOccurred.ToNumber,
      eventOccurred.TextAsByteArray,
      eventOccurred.ImageAsByteArray,
 extension),
           eventOccurred.Subject); 
    response = sender.Submit(mmsMessageDetails);  
}  
catch (Exception ex)  
{ 
    log.Error("cannot send mms message, context =  
➥ [" + eventContext + "]", ex);
 return -1;  
}  
if (log.IsInfoEnabled)  
{ 
    var responseMessage = (null == response) ?  
➥  "null" : response.ToString();
    log.Info("sent mms with event context = 
➥  [" + eventContext + "], response = [" + responseMessage + "]"); 
} 
return 0;
 }  

该方法将外部消息转换为服务内部使用的数据结构,并添加外部合同中未出现的缺失信息。

3.2 Parallel Pipelines模式

对于视频通话的图像搜索,由于其是无客户端服务,需要有状态的服务来处理视频流和维护客户端状态。我们应用了Parallel Pipelines模式,将处理过程分解为子任务,在子任务之间添加队列,并使每个子任务成为独立的组件。

具体来说,RTP Listener接受H.263格式的RTP视频和用户的按键,根据解码结果将数据发送到Identification服务进行图像搜索或Call Flow服务决定向用户显示的内容,Player根据Call Flow服务的决策向用户显示信息。

以下是该模式的mermaid流程图:

graph LR
    A[RTP Listener] -->|图像数据| B[Identification]
    A -->|按键数据| C[Call Flow]
    C -->|显示决策| D[Player]
3.3 Gridable Service模式

为了解决基于图像搜索的核心问题,我们引入了Gridable Service模式,将网格技术应用于服务,以处理计算密集型任务。

Identification服务作为网格根,将搜索任务分配到注册在网格上的不同机器。每台机器上有一个Identification Worker服务实例,包含本地数据库(使用Cassandra)和计算代理(Identifier Node和Worker组件),用于对数据库进行图像搜索。

以下是检查当前工作节点数量的代码:

public class HandlersRefresher  
{ 
    private readonly IFiber dispatcherFiber; 
    private const int HandlersUninitializedValue = -1; 
    private int idealNumberOfHandlers = HandlersUninitializedValue; 
    private static readonly ILog log =  
➥  LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
    public const int RefreshIntervalInMs = 30*1000;
    public const int TimeToFirstCheckInMs = 10*1000;
    private readonly Action<bool> refreshMatchersAction;
    private ITimerControl timerControl = null;
    public HandlersRefresher( 
        IFiber dispatcherFiber, 
        Action<bool> refreshMatchersAction) 
    {  
        if (dispatcherFiber == null)  
            throw new ArgumentNullException("dispatcherFiber");  
        if (refreshMatchersAction == null)  
            throw new ArgumentNullException("refreshMatchersAction");  
        this.dispatcherFiber = dispatcherFiber;  
        this.refreshMatchersAction = refreshMatchersAction; 
    } 
    public int CurrentIdealNumberOfHandlers 
    {  
        get { return this.idealNumberOfHandlers; } 
    } 
    public void InspectHandlers(IFrameHandler[] handlers) 
    {  
        var numberOfHandlers = handlers == null ? 0 : handlers.Length;  
        ScheduleRefreshIfNotAlreadyScheduled();  
        if (numberOfHandlers > 0  
➥ && numberOfHandlers >= idealNumberOfHandlers) 
        {  
            log.DebugFormat("Setting the ideal number of handlers  
➥to {0}", numberOfHandlers); 
            idealNumberOfHandlers = numberOfHandlers;  
        } 
    } 
    private void CancelRefreshSchedulingIfExists() 
    {  
        if (timerControl == null) 
            return;  
        log.Debug("Stopping scheduled handlers refresh");  
        timerControl.Cancel();  
        timerControl = null; 
    } 
    private void ScheduleRefreshIfNotAlreadyScheduled() 
    {  
        if (timerControl != null) 
            return;  
        log.DebugFormat("Scheduling handlers refresh every  
➥{0} ms.", RefreshIntervalInMs);  
        timerControl = dispatcherFiber.ScheduleOnInterval( 
           () => refreshMatchersAction(false),  
              TimeToFirstCheckInMs, 
              RefreshIntervalInMs); 
    }  
}  
4. 系统通信模式
4.1 Inversion of Communications模式

系统通信机制的核心是一个名为eventBroker的软件组件,它实现了Inversion of Communications模式。通过将SOA与事件驱动架构(EDA)相结合,允许服务发布其内部发生的事件流,而不是显式调用其他服务。

以下是Call Flow服务发送优惠券请求的代码:

public void SendCoupon(string title, String targetUri)  
{  
    var recipient = callerPhoneNumber.Value;  
    Logger.DebugFormat("sending sms to '{0}', title is '{1}',  
➥ content is '{2}'", recipient, title, targetUri); 
    SendCouponRequest evt = new SendCouponRequest() 
    { 
        Recipient = recipient, 
        Content = new Uri(targetUri), 
        Title = title 
    };  
    eventBroker.RaiseEvent<SendCouponRequest>(evt);  
}  
4.2 Service Bus模式

eventBroker实际上是一个总线,实现了Service Bus模式,使用统一的消息基础设施进行消息转换、中介、路由和调用。它将业务逻辑与事件的目标信息以及消息发送的实现细节隔离开来,还能封装失败,进行重试等操作。

4.3 Saga模式

Saga模式将服务交互(业务流程)分解为多个较小的业务动作和反动作,并根据消息和超时进行协调和管理。

以下是Call Flow服务发送播放电影请求的代码:

public void PlayMovie(  
    String mediaLocation,  
    bool loop,  
    string interactionID)  
{ 
    var playMovReq = new PlayMovieRequest(SessionId, mediaLocation,  
➥  loop, interactionID);
    if (Logger.IsDebugEnabled)
    {
        Logger.Debug("in saga [" + playMovReq.SessionID + "]  
➥  about to play movie '" + mediaLocation.ToString() 
➥   + "', loop = " + loop.ToString() +  
➥ " interaction ID = '" + interactionID + "'");
        eventBroker.RaiseSagaEvent(playMovReq);  
    }  
}  

在故障场景中,如RTP Listener向Call Flow服务发送事件失败时,eventBroker会引发Saga - Fault事件,Call Recovery服务订阅该事件并尝试处理故障,通过分配另一个Call Flow实例来完成通话。

4.4 Reservation模式

由于部分服务容量有限,需要验证其作为参与者的资格,我们实现了Reservation模式。eventBroker尝试连接它认为空闲的服务,并进行重试,直到确保获得所有需要的服务。

以下是处理预留的代码:

private IEnumerable<Uri> Reserve(IEnumerable<ProxyWrapper> wrappers)  
{ 
    var timeToComplete = DateTimeOffset.Now + TIMEOUT; 
    var uris = Reserve(wrappers,  
        MAX_RETRIES, 
        new List<Uri>(), 
        timeToComplete); 
    return FinalizeReservation(uris)  
}  

private IEnumerable<Uri> Reserve(IEnumerable<ProxyWrapper> wrappers,  
    int retries,  
    ICollection<Uri> failedUris,  
    DateTimeOffset timeToComplete)  
{ 
    var success = true;  
    var newUris = GetCandidateUris(wrappers, failedUris);  
    if (null == newUris) return null;  
    if (newUris.Count == 0) return newUris;  
    failedUris = TryReserveNewMembers(newUris);  
    if (failedUris.Count > 0) 
        success = false;  
    if (DateTimeOffset.Now > timeToComplete) 
        success = false;  
    if (!success && retries == 0) 
        return null;  
    if (!success && retries > 0) 
        return Reserve(wrappers, 
            retries - 1, 
            failedUris, 
            timeToComplete);  
    return newUris;  
}  

private ICollection<Uri> TryReserveNewMembers(IEnumerable<Uri> newUris)  
{ 
    var notifier = new SagaNotifier(Id, 
        Route, 
        newUris, 
        OptionalMembers, 
        LocalUri, 
        Allocator); 
    var failedUris = notifier.ReserveAll(); 
    return failedUris;  
}  
5. 系统可用性模式
5.1 Service Instance模式

对于一些有状态的服务,特别是3G视频通话的视觉搜索服务,我们采用了Service Instance模式,部署多个服务业务逻辑实例。这样做的主要原因是考虑到可用性和故障处理。

例如,在视频通话中,使用一个Call Flow服务处理多个通话时,一个通话的故障可能导致整个服务失败,影响所有通话。而每个呼叫者使用一个Call Flow服务可以提供更好的隔离,一个故障仅影响一个呼叫者,并且减少了多线程和多租户问题。

同时,在识别网格中,我们也为单个计算实例(Identification Worker服务)实现了该模式,以在故障情况下提供更好的隔离,并使计算更接近数据以提高速度。

5.2 Service Watchdog模式

Service Watchdog模式用于监控服务的内部状态,处理潜在问题,尝试自我修复,并持续发布其状态。

以下是管理服务中运行的代理的Watchdog代理类代码:

public class WatchedServiceAgentProxy : IWatchedServiceAgent, IDisposable  
{ 
    internal readonly Uri agentAddress; 
    private IWatchedServiceAgent agentProxy = null; 
    private int failures = 0;  
    private static readonly ILog log =  
➥ LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);  
    private const int MAX_FAILURES = 3;  
    private readonly ResourceContractInfo resourceContractInfo;  
    public WatchedServiceAgentProxy( 
        ResourceContractInfo resourceContractInfo, 
        int instanceIdentifier)  
    { 
        this.resourceContractInfo = resourceContractInfo;  
        agentAddress = AgentAddressProvider.GetAddress(  
➥ resourceContractInfo, instanceIdentifier);
    } 
    public Uri Address 
    {
        get { return agentAddress; }  
    } 
    public void Dispose()  
    { 
        if(this.agentProxy != null) 
            ((ICommunicationObject)agentProxy).Abort();  
    }  
    void EnsureAgentProxy()  
    { 
        if (agentProxy == null) 
            agentProxy = 
            ChannelFactory<IWatchedServiceAgent>.CreateChannel( 
                new NetNamedPipeBinding(),  
                new EndpointAddress(agentAddress)); 
    }  
    void RenewFaultedAgentProxy()  
    { 
        if(agentProxy != null)  
            ((ICommunicationObject)agentProxy).Abort(); 
        agentProxy = null; 
        EnsureAgentProxy(); 
    } 
    public LivelinessResult IsAlive() 
    { 
        try 
        { 
            EnsureAgentProxy(); 
            var isAlive = agentProxy.IsAlive(); 
            return isAlive; 
        } 
        catch(CommunicationException ex) 
        { 
            throw new ServiceCommunicationException  
➥ ("Call to IsAlive failed", ex);
        } 
    } 
    public string GetName() 
    { 
        EnsureAgentProxy(); 
        return agentProxy.GetName(); 
    } 
    public void Shutdown() 
    { 
        EnsureAgentProxy(); 
        agentProxy.Shutdown(); 
    }  
}  

通过这些模式的综合应用,我们构建了一个灵活、可扩展且高可用的系统,能够满足不断变化的业务需求。

基于SOA架构的系统构建与模式应用(下半部分)

6. 系统模式应用总结

在构建系统的过程中,我们综合应用了多种SOA模式,这些模式在系统的不同方面发挥了重要作用,以下是对这些模式应用的总结:
| 模式名称 | 应用场景 | 作用 |
| — | — | — |
| Edge Component | 服务实现、代码层面 | 增加灵活性,分离业务逻辑与其他关注点,将外部协议转换为内部协议 |
| Parallel Pipelines | 视频通话处理 | 将处理过程分解为子任务,提高处理效率和可维护性 |
| Gridable Service | 图像搜索 | 引入网格技术,处理计算密集型任务 |
| Inversion of Communications | 系统通信 | 结合SOA与EDA,允许服务发布事件流 |
| Service Bus | 系统通信 | 实现统一的消息基础设施,进行消息转换、中介、路由和调用 |
| Saga | 业务流程管理 | 将服务交互分解为多个小业务动作和反动作,处理故障场景 |
| Reservation | 服务资源管理 | 为服务提供内部资源的保障 |
| Service Instance | 有状态服务 | 部署多个服务实例,提高可用性和处理故障能力 |
| Service Watchdog | 服务监控 | 监控服务内部状态,处理潜在问题,自我修复并发布状态 |

7. 系统模式间的协同工作

这些模式并非孤立存在,而是相互协同工作,共同构建了一个完整的系统。例如,在视频通话的图像搜索场景中:
- Edge Component模式 :通过不同的网关(如RTP Listener)将外部的视频流和按键信息转换为系统内部可处理的格式,为后续的处理提供基础。
- Parallel Pipelines模式 :将处理过程分解为多个子任务,RTP Listener将解码后的数据发送到Identification服务进行图像搜索,发送到Call Flow服务进行决策,Player根据决策向用户显示信息,各个子任务独立运行,提高了处理效率。
- Gridable Service模式 :Identification服务作为网格根,将搜索任务分配到不同的机器上,利用网格技术处理计算密集型的图像搜索任务。
- Inversion of Communications模式 :服务之间通过eventBroker发布和接收事件,实现异步通信,避免了显式调用带来的耦合问题。
- Service Bus模式 :eventBroker作为总线,负责消息的转换、中介、路由和调用,将业务逻辑与消息处理隔离开来。
- Saga模式 :当出现故障时,如RTP Listener向Call Flow服务发送事件失败,会触发Saga - Fault事件,Call Recovery服务会尝试处理故障,确保业务流程的正常进行。
- Reservation模式 :在分配服务资源时,确保有足够的服务实例可用,避免资源不足导致的问题。
- Service Instance模式 :为每个呼叫者分配一个Call Flow服务实例,提高了服务的隔离性和可用性,减少了故障的影响范围。
- Service Watchdog模式 :监控各个服务的状态,及时发现并处理潜在问题,保证系统的稳定运行。

以下是这些模式在视频通话图像搜索场景中的协同工作mermaid流程图:

graph LR
    A[Edge Component(RTP Listener)] -->|数据转换| B[Parallel Pipelines]
    B -->|图像搜索任务| C[Gridable Service(Identification)]
    B -->|决策任务| D[Call Flow]
    D -->|显示决策| E[Player]
    F[Inversion of Communications(eventBroker)] <-->|事件发布/接收| B
    F <-->|事件发布/接收| C
    F <-->|事件发布/接收| D
    F <-->|事件发布/接收| E
    G[Service Bus(eventBroker)] <-->|消息处理| F
    H[Saga] <-->|故障处理| F
    I[Reservation] <-->|资源分配| F
    J[Service Instance(Call Flow)] -->|提高可用性| D
    K[Service Watchdog] -->|监控状态| B
    K -->|监控状态| C
    K -->|监控状态| D
    K -->|监控状态| E
8. 系统模式应用的优势

通过应用这些SOA模式,系统获得了以下优势:
- 灵活性和可扩展性 :SOA的灵活性允许我们根据业务需求的变化,轻松添加新的服务或修改现有服务的内部结构。例如,当业务增加对短信和电子邮件的需求时,我们可以通过添加相应的网关服务来满足需求。
- 高可用性和故障处理能力 :Service Instance模式和Service Watchdog模式的应用,提高了系统的可用性和故障处理能力。在视频通话场景中,每个呼叫者使用一个Call Flow服务实例,一个故障仅影响一个呼叫者,同时Service Watchdog可以及时发现并处理服务故障。
- 松耦合和可维护性 :Inversion of Communications模式和Service Bus模式的使用,将服务之间的耦合度降低,使得服务可以独立开发、部署和维护。同时,Parallel Pipelines模式将处理过程分解为子任务,提高了代码的可维护性。
- 资源优化 :Gridable Service模式和Reservation模式的应用,优化了系统资源的使用。Gridable Service模式利用网格技术处理计算密集型任务,提高了计算效率;Reservation模式确保了服务资源的合理分配。

9. 系统模式应用的挑战与应对

在应用这些模式的过程中,我们也遇到了一些挑战,以下是这些挑战及相应的应对措施:
| 挑战 | 应对措施 |
| — | — |
| 模式的复杂性 | 深入理解每个模式的原理和应用场景,在设计和实现过程中进行充分的规划和测试 |
| 服务间的协调 | 通过Inversion of Communications模式和Service Bus模式,实现服务之间的异步通信和消息路由,减少服务之间的直接依赖 |
| 资源管理 | 采用Reservation模式,对服务资源进行合理分配和管理,避免资源不足或浪费 |
| 故障处理 | 利用Saga模式和Service Watchdog模式,处理故障场景,确保业务流程的正常进行 |

10. 未来展望

随着业务的不断发展和技术的不断进步,系统可能会面临更多的挑战和需求。未来,我们可以考虑以下方面的改进和扩展:
- 引入新的模式 :随着技术的发展,可能会出现一些新的SOA模式,我们可以根据业务需求引入这些新模式,进一步提高系统的性能和功能。
- 优化现有模式 :对现有的模式进行优化,例如改进Gridable Service模式的网格管理算法,提高计算效率;优化Service Watchdog模式的监控策略,提高故障发现和处理的及时性。
- 集成新技术 :将系统与新的技术进行集成,如人工智能、大数据等,为业务提供更强大的支持。例如,利用人工智能技术提高图像搜索的准确性,利用大数据技术对系统的运行数据进行分析,优化系统的性能。

通过不断地改进和扩展,我们可以使系统更加适应业务的发展,为用户提供更好的服务。

综上所述,通过综合应用多种SOA模式,我们构建了一个灵活、可扩展、高可用的系统,在满足业务需求的同时,也为未来的发展奠定了坚实的基础。在实际应用中,我们需要根据具体的业务场景和需求,合理选择和应用这些模式,不断优化系统的性能和功能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值