通过Mailgun 发送的 EDM 邮件被邮件服务商邮件被视为垃圾邮件怎么办

登录以解锁更多InfoQ新功能
获取更新并接收通知
给您喜爱的内容点赞
关注您喜爱的编辑与同行
966,690 八月 独立访问用户
语言 & 开发
架构 & 设计
文化 & 方法
您目前处于:
日邮件发送量千万级,谈京东的EDM平台优化之路
日邮件发送量千万级,谈京东的EDM平台优化之路
0&他的粉丝
日. 估计阅读时间:
不到一分钟
硅谷人工智能、机器学习、互联网金融、未来移动技术架构 ,
相关厂商内容
相关赞助商
老平台存在的问题如下:
1、技术栈、实现思路混乱
受限于业务,老平台分成了生产邮件和促销邮件两个子平台。对于邮件任务的发送,老促销邮件平台引入了Thrift框架,而老生产邮件平台则通过抢占式更新数据库的方式实现。
渲染邮件模板时,老生产使用的是velocity,而老促销已经改为由上层(邮件模板装修系统)用handlebars提前渲染好后直接发送。
2、SQL Server的改造需求。
老平台的数据库使用的是SQL Server,从降低公司成本的角度出发,有尽快迁移到MySQL的需求。
3、历史需求包袱很重。
很多业务已经下线,但是代码还留在线上(包括各种关联系统)。
新平台:业务架构的确定
在这样的情况下,我们决定抛弃老的平台,构建新一代的EDM平台。那其中第一步就是确认业务架构。
新平台将由生产平台、发送平台和统一管理平台组成。生产平台负责生产邮件发送任务,发送平台则部署在主流运营商的网络上,冗余部署一定量的发送节点,保证发送成功率。因为邮箱服务提供商通常会对邮件的发送方按IP做流控限制,并且对各种网络运营商投递的邮件的接受程度也不一样。
同时,为了保证在主流的网络运营商渠道上都有发送节点,避免因某个网段或某个运营商网络故障引起邮件发送成功率的波动。所以将发送和生产的逻辑区分开,有利于应用区分部署和扩容,平台间的职责也更加清晰。
老平台也是生产和发送分成两个平台,只不过职责没有划分得非常清晰,比如各自的平台上都集成了管理功能,发送节点只能调节自己的发送策略,不利于发送策略的批量调整。
生产平台和发送平台间使用Redis队列传递邮件任务。设计期间考虑过MQ,Kafka类组件,没有使用的原因是以上组件对于传递邮件的任务都&过重&,引入新的组件意味着新的风险,新组件的稳定性也会影响平台的稳定性。同时Redis对队列有原生的支持,作为当前最常使用的组件,起简单易用的特点正好符合新架构对这个组件的要求。再加上公司已经对Redis实现了集群及自动伸缩方案,可用率大大提高,学习零成本,都是Redis的优势,也是这次架构升级选用Redis的理由。
建立统一管理平台,实现对生成和发送情况的统一调度。要求是实现对整个生产和发送平台的调度管理,包含指定高优节点,屏蔽成功率较低节点,降级开关等一系列措施。
经过多次梳理和方案推导后,整个EDM平台的业务架构如下:
生产平台对接用户组,订单组,会员等系统的MQ消息或远程调用生成邮件发送任务,并按类型划分1-10个优先级,数字越大,优先级越高。
Redis按优先级和目标邮箱两个维度拆分成多个Redis队列,负责将邮件任务传递到发送平台。
发送平台聚焦在发送业务的处理上,包括优先级队列、发送降频、邮件模板渲染等事务。
管理平台主要负责配置数据的维护、降级开关的推送和必要时人工对生产、发送平台的调度。
1、Redis优先级队列
首先,业务上决定了邮件任务的紧急程度是不一样的,例如用户对账户密码找回的邮件就比优惠券到账的及时性敏感度更高,显然密码找回的邮件需要以最快速度投递到用户邮箱中。这和某些在极端场景才出现的&优先级&不一样,是一直持续存在的高优任务,最简单的办法就是区别对待,按优先级设置队列,从生产平台开始到Redis队列再到发送平台都一直是一个或多个特殊的队列,方便系统对这些高优发送任务做处理。
同样,高优先级队列长度的报警阀值比较小,一旦积压研发同学会第一时间收到报警,必要时可以人工接入。而发送平台总是最先拉取高优先级发送任务,保证其第一时间被处理。值得注意的是,从客观规律上看高优的邮件往往量是比较小,这使得发送平台总是优先处理高优的邮件并不会让优先级低的发送任务没有机会被拉取。
当业务量较大,发送较为频繁时触发邮件服务商的流控,或网络不稳定,出口IP异常时都会引起部分发送平台的邮件投递成功率的下降,这时需要让成功率将低的节点暂时甚至永久的不再向邮件服务商投递邮件,解决方案之一是在队列的拆分维度除了优先级以外再增加一个目标邮箱,一旦出现上述问题后,可以直接让发送节点不再拉取该邮箱的所有队列来实现故障隔离。
同时,用优先级和目标邮箱拆分Redis队列还有一个好处是,如果使用的是分布式的Redis,队列的元素总是在一个分片中的,如果队列过少,会导致有可能大量元素都集中在同一个分片中形成热点分片。将Redis队列拆分后可以让分个分片的读写相对更均衡,分片的利用率更高。实际上,Redis队列还设置了一个最大长度,防止队列无限制的增长。
2、投递降频
投递邮件时如果投递被拒绝邮件服务提供商一般都会返回一个错误码,发送平台上有一个错误映射表,发送错误后将错误码和错误映射表比较,如果触发了流控则降低邮件发送任务的拉取频率,直到投递成功率恢复后再逐步提升发送能力。
3、Checker
Checker是生产平台扫描邮件发送任务的定时任务的总称,按职责不同,Checker被具体分为UnsuccessChcker、InQueueChecker、ExpireChecker等等。职责是将各个状态的邮件发送任务更新为下一个流程需要的状态,比如将入库成功的状态更新为Redis队列中,Redis队列中更新为发送平台发送中,发送错误的任务更新为重新投递等等。
重新投递是非常重要的一个功能,因为某个出口IP因为各种原因可能会常常被邮件服务商拒收,重试相当于有较大几率更换出口IP再做发送尝试,有利于投递成功率的提高。因为邮件发送任务常常被各种Checker更新,为了保证数据的一致性和状态按正确流程流转,邮件发送任务被加上了版本号,每次更新后自增,更新时使用乐观锁更新。
初期平台上线时的第一版架构如下:
上线后出现了一系列的性能问题,总结起来主要为两类。
第一类问题:写库CPU 100%,影响远程调用接口的性能,引发上游团队关注。
引发写库CPU 100%的原因最主要的还是数据库同一时间读写请求太多和索引利用率不高导致的。因此第一步想将数据库的读写压力分开,对数据库做了读写分离,所有的读请求全部调整到了从库上去,以此降低主库压力。这里有一个前提是经过评估,从库读取脏数据并不会对业务产生困扰,因为邮件发送任务本身有版本号,即使数据库主从同步有延迟引起从库读到&脏数据&使用乐观锁更新时也会失败,不会引起业务错误。
第二步将查询条件归一再后建立索引,索引不是越多越好,归一时可以现将查询任务列出来,观察哪些查询条件是相似的,有没有特殊的业务导致了不一样的查询条件,这些业务有没有办法从其他角度去支持,逐步归纳后再建索引。上述步骤多次使用后,通常情况必须要建立的索引就不会太多了。目前邮件发送任务表(分表后单表千万级)只有一个主键索引和一个组合索引(三个字段),所有查询条件全部先利用索引查询数据。
调整完索引后发现Checker的扫描还是过于频繁,读库CPU利用率还是不够理想(过高),梳理业务后发现整个平台的发送能力取决于邮件服务商的接受量和一定程度的发送平台量(出口IP量)。两者不变的情况下,整个发送平台的发送量不会提升,Redis队列的吞吐能力也不变,Checker大部分时候运行的结果只是Push了个别元素,甚至没有Push。Checker完全可以改成Redis队列小于一定阀值,例如最大长度的1/2再做一次扫描,一次扫描尽量将队列填满。调整Checker策略和索引后数据库的QPS大约降了2/3,load也稳定在5以下。
还有一个让数据库CPU较高的原因是Checker引起数据库死锁。&
例如有Transaction1需要对ABC记录加锁,已经对A,B记录加了X锁,此刻正尝试对C记录加锁。同时此前Transaction2已经对C记录加了独占锁,此刻需要对B记录加X锁,就会产生数据库的死锁。
尽管MySQL做了优化,比如增加超时时间:innodb_lock_wait_timeout,超时后会自动释放,释放的结果是Transaction1和Transaction2全部Rollback(死锁问题并没有解决,如果不幸,下次执行还会重现)。
如果每个Transaction都是Update数万,数十万的记录(我们的业务就是),那事务的回滚代价就非常高,还会引起数据库的性能波动。
解决办法很多,比如先查询出数据后再做逐条做写操作,或者写操作加上一个limit限制每次的更新次数,同时避免两个Transaction并发执行等等。最终在调整了Checker的运行周期后选择了逐条更新的方案,因为业务对于时间上要求并不高,更新不及时并不会引起业务上的错误。
经过以上优化后,压测表明整个平台3倍峰值流量下,数据库CPU利用率10%以下,load5以下,95%的远程调用只有一次数据库的Insert操作,远程接口TP999在20ms内。
第二类问题是因为代码编写不当,引发JVM假死和CPU 100%。
出于减少远程接口同步逻辑的需要,研发同学将大部分操作改为异步方式,比如邮件的推荐商品服务。因为邮件发送任务在生产平台到发送平台间流转需要一定的时间,将推荐商品服务异步化后,生产邮件发送任务的同步逻辑会减少,远程接口调用或MQ消息消费线程可以更早返回,对推荐接口的性能波动容忍度也会变高,只需要保证在发送平台渲染邮件模板前能够拿到推荐商品的数据即可。
异步改造时,研发同学使用了线程池的无界队列,并因为一个低级BUG导致上线后无界队列的消费线程只有5个,生产和消费的速率严重不匹配,导致了短时间内JVM内存占用过高,JVM频繁GC,JVM频繁处于&stop the world&阶段,呈现出&假死&状态,最终再次影响到远程接口的调用和MQ消息的消费。这次的经验说明,实例宕机或许并不是最难处理的,更难处理的是实例处于可以提供服务,但是没有服务能力的状态。
我们的解决方案是使用有界队列,防止超长队列的产生。设置队列的拒绝策略,队列无空闲位置时,放弃入队操作。此时会导致部分邮件缺少推荐商品模块,可视作推荐商品模块的处理能力达到上限后的一种降级方式。
Redis队列中元素的大小大于10K时,入队和出队的效率会严重下降,出于这这个原因,Redis队列中只存放有邮件发送任务的原始数据,渲染工作是在发送节点上完成的。发送高峰期时,发送平台的CPU利用率整体在80%甚至90%以上,发送能力无法再提升。经过一系列排查后发现CPU利用率较高的源头来自于Handlebars模板渲染模块。
抽样查看部分线上机器的线程占用率时发现渲染线程大部分时间一直在做邮件模板的语法解析,参考相应文档后发现语法解析是模板渲染中最耗时的流程,为了提高效率无论是Velocity还是Handlbars都会对模板语法解析的结果做缓存,下次渲染时直接使用解析结果渲染。但缓存是基于VelocityEngine或Handlebar实例的,如果JVM中存在多个VelocityEngine或Handlebar实例,缓存就无法有效利用,结果是每次渲染模板时都要做语法解析,如果并发解析的线程达到数十、数百个的话,就会引起实例的CPU 100%。
解决方案:
保证全局只有一个Handlebar实例,方便共享缓存结果。
容器启动时,渲染线程依次启动并等待一段时间在启动下一个渲染线程,避免并发启动多个线程时再次出现并发解析模板的情况。
除去以上问题的解决,上线后研发团队还做了几次全流程的优化,优化包括黑名单、退订数据缓存化,Redis队列Push方式异步化、批量化,发送平台的拉取合并、邮件模板本地化等。
优化后平台的应用架构如下:
容灾方案中,优先考虑的就是多网络运营商覆盖的问题,防止某一网络运营商网络故障影响邮件发送的能力。目前的方案是单一机房配置单一网络运营商的出口IP及反解析域名,每个机房部署相应的生产平台,Redis队列和发送平台,彼此之间相互独立运行,底层使用同一个数据库,生产平台提供的远程接口为同一个别名服务,MQ消息也是消费的同一个Topic下的内容,多个Redis之间存在少量数据的同步,比如去重数据。整体架构如下:
我们将紧急情况分为内部接口、服务故障和外部服务故障。针对内部故障,比如:
Redis集群故障。如果是单分片故障,Redis集群提供了主从分片,可以通过切换分片的方式解决。如果是集群整体故障,可以启用备用Redis集群,在这里不存在集群数据为空的问题,因为生产平台有Checker存在,如果切换集群,Checker可以感知到Redis队列数据量不够,会重新将待发送的邮件任务Push到Redis队列中。
针对外部故障,比如:
机房出口网络故障。可以停止故障机房的发送平台,因数据库共享,数据入库后对端机房的Checker会将数据重新Push到对端机房的Redis队列中,从对端机房发送邮件任务。这里还有一种方案是修改故障机房的Redis集群配置,故障机房的生产平台生成邮件发送任务后直接将数据Push到对端机房的Redis集群中,省略Checker扫描的这一步,会大大减少数据库的读压力。
邮件服务提供商对部分出口IP降频。发送节点上内置了降频处理措施,可以解决该问题。
邮件服务商屏蔽部分出口IP。通过自研的配置推送与服务监控框架,可用管理平台将被屏蔽的IP地址推送到发送平台上,发送平台通过比对如果发现自身已被屏蔽,将不再从Redis队列中Pull相应的邮件发送任务。
EDM平台上层对接了精准营销平台和生产邮件业务,如何用一套通用的解决方案解决两个业务的不同需求是建设该平台的难点,需要在两种业务形态间找到共性并满足各自业务对及时性,发送量方面的要求。尤其是生产邮件业务,对发送的及时性,稳定性的要求都较高,非常容易引起上层业务团队的关注。在做方案时需要更多的关注到架构本身对性能、容灾、业务和研发同学的友好性,架构越容易让人接受,更简单的解决现有问题,才有可能在以后的发展中不断往好的方向进化,容纳更多复杂的业务需求,支持业务的长久发展。
刘锟洋,独立博主,系统架构师。就职于京东成都研究院,做过A/B Test,精准营销平台,会员营销平台。公司内部开源小组发起人,内部开源过多个开源项目,对性能全链路优化和分布式服平台有浓厚兴趣。
感谢对本文的审校。
给InfoQ中文站投稿或者参与内容翻译工作,请邮件至。也欢迎大家通过新浪微博(,),微信(微信号:)关注我们。
Author Contacted
语言 & 开发
23 他的粉丝
架构 & 设计
149 他的粉丝
0 他的粉丝
0 他的粉丝
0 他的粉丝
0 他的粉丝
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
赞助商链接
InfoQ每周精要
订阅InfoQ每周精要,加入拥有25万多名资深开发者的庞大技术社区。
架构 & 设计
文化 & 方法
<及所有内容,版权所有 &#169;
C4Media Inc.
服务器由 提供, 我们最信赖的ISP伙伴。
北京创新网媒广告有限公司
京ICP备号-7
找回密码....
InfoQ账号使用的E-mail
关注你最喜爱的话题和作者
快速浏览网站内你所感兴趣话题的精选内容。
内容自由定制
选择想要阅读的主题和喜爱的作者定制自己的新闻源。
设置通知机制以获取内容更新对您而言是否重要
注意:如果要修改您的邮箱,我们将会发送确认邮件到您原来的邮箱。
使用现有的公司名称
修改公司名称为:
公司性质:
使用现有的公司性质
修改公司性质为:
使用现有的公司规模
修改公司规模为:
使用现在的国家
使用现在的省份
Subscribe to our newsletter?
Subscribe to our industry email notices?
我们发现您在使用ad blocker。
我们理解您使用ad blocker的初衷,但为了保证InfoQ能够继续以免费方式为您服务,我们需要您的支持。InfoQ绝不会在未经您许可的情况下将您的数据提供给第三方。我们仅将其用于向读者发送相关广告内容。请您将InfoQ添加至白名单,感谢您的理解与支持。首先你需要保证您的内容不是垃圾邮件的内容,第二控制您发往邮件服务商的速度,第三选择比较靠谱的第三方邮件代发。一般第三方邮件会提供发送的提供SPF和DKIM的认证,如果提供独立ip发送,你需要保证你的信誉度,&br&这样就使得你发送的邮件的到达率比较高。推荐邮件发送平台产品国外的可以选择mailgun,
mandrill, sendgrid等。国内的就是SendCloud了。
首先你需要保证您的内容不是垃圾邮件的内容,第二控制您发往邮件服务商的速度,第三选择比较靠谱的第三方邮件代发。一般第三方邮件会提供发送的提供SPF和DKIM的认证,如果提供独立ip发送,你需要保证你的信誉度, 这样就使得你发送的邮件的到达率比较高。推…
官网写的那么清楚看不到?&br&Emails
Price / 1,000
Total Price
Next 500,000
Next 1,000,000
Next 5,000,000
Any Additional
官网写的那么清楚看不到? Emails Price / 1,000 Total Price 0 - 10,000 Free Next 500,000 $0.50 Next 1,000,000 $0.35 Next 5,000,000 $0.15 Any Additional $0.10
这边有个推荐的,是arpReach, 不过是收费的,但他不会显示由谁代发,直接就是你的邮箱作为发信人。
这边有个推荐的,是arpReach, 不过是收费的,但他不会显示由谁代发,直接就是你的邮箱作为发信人。
我设计的知乎的 EDM 系统。
&br&&br& @周源 说的很对,首先你要保证你群发的邮件内容确实不是垃圾,不然再好的技术手段都无法帮你解决送达的问题,因为真的垃圾邮件最终是会被用户标记出来然后被邮件系统过滤掉。至于你的邮件是不是垃圾,你需要想清楚用户是否愿意阅读你发的邮件、有些用户是否会点击“这是一封垃圾邮件”的按钮。有些用户可能对你的邮件感兴趣、有些可能不是,所以在你发送的每封邮件中都一定要在容易发现的位置(通常是邮件尾部)用简单易懂的方式给用户退订。
&br&&br& 在确保了你的内容不是垃圾后,技术上有很多需要注意的事项,一下列出几个最重要而且也不难做到的:
&ul&&li&&b&Blacklist&/b&:确保你发送邮件的服务器 IP、域名、所有者不在任何一个垃圾邮件黑名单里。有个很方便的小工具 [1] 可以快速在各大黑名单(比如 Spamhaus)里查找你的记录。“感谢”唯利是图的垃圾邮件发送者们(很可能有你哦!),中国大陆的大部分 IP 都会在某些黑名单里出现。本来中国分到的 IPv4 地址就少,所以除非你非常幸运,很少能拿到不在某些黑名单里的 IP 地址。当然你也可以尝试去列有你 IP 的黑名单组织申诉,要求撤销你的黑名单。好好练习英文。&/li&&li&&b&DNS&/b&:发送邮件的服务器要设置正反向 DNS 解析。比如你的邮件服务是在 ,然后
解析得到 IP 是 1.2.3.4,那么反向解析 1.2.3.4 则一定要能得到 。找你的运营商去给你设置反向解析。&/li&&li&&b&SPF/SenderID&/b&:邮件发送者(就是 from 字段)的域名要设置 SPF 和 SenderID,里面要根据情况加入你可能用到的邮件服务器 IP 或者域名。简单来说 SPF 和 SenderID 表明了哪些服务器可以以你的域名的名义发送邮件。深入学习和设置请自行搜索相关教程,此处不再详述。&/li&&li&&b&DKIM&/b&:如果条件允许,你可以使用 DKIM 来对你发送的邮件数字签名,可以让收件方邮件系统(如果它支持 DKIM 的话)确信邮件的确是通过你的域名授权发送的。这和 SPF/SenderID 一样可以降低你的地址被垃圾邮件发送者冒用从而带来的风险。&/li&&li&&b&Bounce/Complaint/Reject&/b&:随时关注你的发送的邮件的回应。接受方的邮件系统通常会返回给你一些邮件告知为何你的邮件不能成功送达,除开接受邮件地址错误等因素,特别关注对方抱怨你的邮件是垃圾的情况。如果你还想继续稳定安全的发送邮件的话,&b&&u&不要再向这些地址投递邮件&/u&&/b&!&/li&&/ul& 知乎现在在试用 Amazon SES 的服务。它不能帮你解决所有问题,但比起自己从头做起来还是要简单得多,特别是对于创业公司人手不够的情况。费用也很简单透明,$1/万封 + $0.12/GB 流量费(发得多流量有优惠)。 &br&&br&【更新】:由于一些技术上的原因,知乎现在已经不再使用 Amazon SES 服务了。原因详见: &a href=&/question/& class=&internal&&&span class=&invisible&&http://www.&/span&&span class=&visible&&/question/2008&/span&&span class=&invisible&&8568&/span&&span class=&ellipsis&&&/span&&/a&&br&&br& [1]: &a href=&///?target=http%3A///blacklists.aspx& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&/blacklist&/span&&span class=&invisible&&s.aspx&/span&&span class=&ellipsis&&&/span&&i class=&icon-external&&&/i&&/a&
我设计的知乎的 EDM 系统。 @周源 说的很对,首先你要保证你群发的邮件内容确实不是垃圾,不然再好的技术手段都无法帮你解决送达的问题,因为真的垃圾邮件最终是会被用户标记出来然后被邮件系统过滤掉。至于你的邮件是不是垃圾,你需要想清楚用户是否愿意阅…
已有帐号?
无法登录?
社交帐号登录
302 人关注
1334 条内容
346 人关注
159 人关注
185 条内容
132 人关注
4348 条内容
524 条内容通过 Mailgun 发送的 EDM 邮件被邮件服务商视为垃圾怎么办? - 知乎119被浏览27330分享邀请回答01 条评论分享收藏感谢收起哪种邮件群发软件最好用?不骗人,能免费发邮件.群发邮件进收件箱.邮件群发原理.
亚云邮件营销软件(专业外贸邮件群发),copyrightEDM Lifecycle Marketing
EDM Lifecycle Marketing
EDM Lifecycle Marketing
Background_Bottom}

我要回帖

更多关于 邮件被视为垃圾邮件 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信