.net core实践系列之短信服务-架构优化(一)

本文涉及的产品
数字短信套餐包(仅限零售电商行业),100条 12个月
短信服务,100条 3个月
国际/港澳台短信套餐包,全球plus 100条 6个月
简介: .net core实践系列之短信服务-架构优化(一)

前言


通过前面的几篇文章,讲解了一个短信服务的架构设计与实现。然而初始方案并非100%完美的,我们仍可以对该架构做一些优化与调整。


同时我也希望通过这篇文章与大家分享一下,我的架构设计理念。


源码地址:https://github.com/SkyChenSky/Sikiro.SMS/tree/optimize (与之前的是另外的分支)


架构是设计的还是演变的?


架构


该词出自于建筑学。软件架构定义是指软件系统的基础结构,是系统中的实体及实体(服务)之间的关系所进行的抽象描述。而架构设计的目的是为了解决软件系统复杂度带来的问题。


复杂度


系统复杂度主要有下面几点:


  • 高可用
  • 高性能
  • 可扩展
  • 安全性
  • 维护成本
  • 用户规模


业务规模


系统的复杂度导致的直接原因是业务规模。为了用户流畅放心的使用产品,不得不提高系统性能与安全。当系统成为人们生活不可缺一部分时,避免机房停电、挖掘机挖断电缆导致的系统不可用,不得不去思考同城跨机房同步、异地多活的高可用方案。


答案并非二选一


我认为架构,需要在已知可见的业务复杂度与用户规模的基础上进行架构设计;伴随着技术积累与成长而对系统进行架构优化;用户的日益增长,业务的不断扩充,迫使了系统的复杂度增加,为了解决系统带来新的复杂度而进行架构演变。


因此,架构方案是在已有的业务复杂度、用户规模、技术积累度、人力时间成本等几个方面的取舍决策后的结果体现。


原架构


image.png


缺点分析


  • 一般情况下,调度任务轮询数据库,90%的动作是无用功,频繁的数据库访问会对数据库增加不少压力。
  • 为了让调度任务服务进行轮循数据,需要在API优先进行数据持久化,这无疑是降低了API的性能。
  • MongoDB的Update操作相比于Insert操作时低效的,对于日志类数据应增量添加。


因此从上述可见,调度任务服务这块是优化关键点所在。


新架构图


image.png


  • 使用了RabbitMQ的队列定时任务代替调度任务来实现定时发送。
  • 抛弃了调度任务,减少了调用链,同时也减少了应用服务数据量。
  • 对SMS集合在MongoDB里进行按年月的时间划分,对于日志类数据可以在有效的时间范围外进行方便的归档、删除。同时也避免了同集合的数据量过大导致的查询效率缓慢。


队列定时任务


RabbitMQ自身并没有定时任务,然而可以通过消息的Time-To-Live(过期时间)与Dead Letter Exchange(死信交换机)的结合模拟定时发布的功能。具体原理如下:


  • 生产者发布消息,并发布到已申明消息过期时间(TTL)的缓存队列(非真正业务消费队列)
  • 消息在缓存队列等待消息过期,然后由Dead Letter Exchange将消息重新分配到实际消费队列
  • 消费者再从实际消费队列消费并完成业务

 

image.png


Dead Letter Exchange


Dead Letter Exchange与平常的Exchange无异,主要用于消息死亡后通过Dead Letter Exchange与x-dead-letter-routing-key重新分配到新的队列进行消费处理。


消息死亡的方式有三种:


  • 消息进入了一条已经达到最大长度的队列
  • 消息因为设置了Time-To-Live的导致过期
  • 消息因basic.reject或者basic.nack动作而拒绝


Time-To-Live


两种消息过期的方式:


队列申明x-message-ttl参数
var args = new Dictionary<string, object>();
args.Add("x-message-ttl", 60000);
model.QueueDeclare("myqueue", false, false, false, args);
每条消息发布声明Expiration参数
byte[] messageBodyBytes = System.Text.Encoding.UTF8.GetBytes("Hello, world!");
IBasicProperties props = model.CreateBasicProperties();
props.ContentType = "text/plain";
props.DeliveryMode = 2;
props.Expiration = "36000000"
model.BasicPublish(exchangeName,
                   routingKey, props,
                   messageBodyBytes);


RabbitMQ.Client队列定时任务Demo


class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory
            {
                HostName = "10.1.20.140",
                UserName = "admin",
                Password = "admin@ucsmy"
            };
            using (var connection = factory.CreateConnection())
            using (var channel = connection.CreateModel())
            {
                var queueName = "Queue.SMS.Test";
                var exchangeName = "Exchange.SMS.Test";
                var key = "Route.SMS.Test";
                DeclareDelayQueue(channel, exchangeName, queueName, key);
                DeclareReallyConsumeQueue(channel, exchangeName, queueName, key);
                var body = Encoding.UTF8.GetBytes("info: test dely publish!");
                channel.BasicPublish(exchangeName + ".Delay", key, null, body);
            }
        }
        private static void DeclareDelayQueue(IModel channel, string exchangeName, string queueName, string key)
        {
            var retryDic = new Dictionary<string, object>
            {
                {"x-dead-letter-exchange", exchangeName+".dl"},
                {"x-dead-letter-routing-key", key},
                {"x-message-ttl", 30000}
            };
            var ex = exchangeName + ".Delay";
            var qu = queueName + ".Delay";
            channel.ExchangeDeclare(ex, "topic");
            channel.QueueDeclare(qu, false, false, false, retryDic);
            channel.QueueBind(qu, ex, key);
        }
        private static void DeclareReallyConsumeQueue(IModel channel, string exchangeName, string queueName, string key)
        {
            var ex = exchangeName + ".dl";
            channel.ExchangeDeclare(ex, "topic");
            channel.QueueDeclare(queueName, false, false, false);
            channel.QueueBind(queueName, ex, key);
        }
    }


目录
相关文章
|
6天前
|
监控 Java 持续交付
后端开发中的微服务架构实践与挑战####
在当今快速迭代的软件开发领域,微服务架构以其灵活性和可扩展性成为众多企业的首选。本文探讨了微服务架构的核心概念、实施策略及面临的主要挑战,旨在为后端开发者提供一个全面的指南。通过分析真实案例,揭示微服务在提升系统敏捷性的同时,如何有效应对分布式系统的复杂性问题。 ####
|
2天前
|
消息中间件 负载均衡 测试技术
后端开发中的微服务架构实践与挑战####
本文旨在探讨微服务架构在后端开发中的应用实践,深入分析其带来的优势与面临的挑战。通过剖析真实案例,揭示微服务转型过程中的关键技术决策、服务拆分策略、以及如何有效应对分布式系统的复杂性问题。文章还将提供一套评估企业是否适合采用微服务架构的框架,帮助读者更好地理解这一架构模式,并为企业的技术选型提供参考。 ####
|
1天前
|
运维 监控 安全
深入理解微服务架构:设计原则、挑战与实践
深入理解微服务架构:设计原则、挑战与实践
|
5天前
|
Cloud Native Devops 持续交付
云原生架构的演进与实践
本文深入探讨了云原生架构的核心概念、技术组件及其在现代软件开发中的应用。通过分析容器化、微服务、持续集成/持续部署(CI/CD)等关键技术,揭示了这些技术如何共同促进应用程序的灵活性、可扩展性和高可用性。文章还讨论了云原生架构实施过程中面临的挑战和最佳实践,旨在为开发者和企业提供一套实用的指导方针,以便更有效地利用云计算资源,加速数字化转型的步伐。
20 5
|
5天前
|
测试技术 持续交付 微服务
深入理解微服务架构:从概念到实践
深入理解微服务架构:从概念到实践
|
5天前
|
负载均衡 Cloud Native 持续交付
云原生时代的微服务架构:优势、挑战与实践
云原生时代的微服务架构:优势、挑战与实践
14 0
|
5天前
|
API 持续交付 云计算
云计算中的微服务架构设计与实践
云计算中的微服务架构设计与实践
|
10天前
|
缓存 负载均衡 JavaScript
探索微服务架构下的API网关模式
【10月更文挑战第37天】在微服务架构的海洋中,API网关犹如一座灯塔,指引着服务的航向。它不仅是客户端请求的集散地,更是后端微服务的守门人。本文将深入探讨API网关的设计哲学、核心功能以及它在微服务生态中扮演的角色,同时通过实际代码示例,揭示如何实现一个高效、可靠的API网关。
|
8天前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
8天前
|
Dubbo Java 应用服务中间件
服务架构的演进:从单体到微服务的探索之旅
随着企业业务的不断拓展和复杂度的提升,对软件系统架构的要求也日益严苛。传统的架构模式在应对现代业务场景时逐渐暴露出诸多局限性,于是服务架构开启了持续演变之路。从单体架构的简易便捷,到分布式架构的模块化解耦,再到微服务架构的精细化管理,企业对技术的选择变得至关重要,尤其是 Spring Cloud 和 Dubbo 等微服务技术的对比和应用,直接影响着项目的成败。 本篇文章会从服务架构的演进开始分析,探索从单体项目到微服务项目的演变过程。然后也会对目前常见的微服务技术进行对比,找到目前市面上所常用的技术给大家进行讲解。
23 1
服务架构的演进:从单体到微服务的探索之旅
下一篇
无影云桌面