后端业务架构开发经验

2016-05-16 fishedee 后端

1 概述

业务模块划分,这是个说起来简单但是又很复杂的问题。

2 原则

  • 高内聚,低耦合
  • keep it simple
  • design for failure

为可维护性而努力,而不是便利性。

3 高内聚,低耦合

3.1 领域编程

面向领域编程,而不是面向数据库编程

3.2 正交分解

如果是可能公开的需要长期维护的接口,形式要尽量简单、参数要尽量少、接口数量也要尽可能少,互相在功能上没有重叠,按照书上的称呼这叫正交。只有这样才能保证未来的接口升级对旧版接口用户的影响尽可能的小。

  • 分页接口要单独为一个接口,不要和其他接口合并在一起
  • 批量接口是性能的关键

3.3 单向依赖

队列与解耦

4 Keep it simple

4.1 刚好能用

为未来而设计,但不要为未来的未来而设计。开发为需求服务,为了适应未来的需求变化,我们会引入设计模式来让后续的开发变得简单快速。可是,如果过度地使用设计模式,过度猜测未来需求的变化,导致代码的适应性过强,接口和实现都变复杂了,反而会得不偿失。因为,未来的需求总是难以猜测的,过度设计的后果很有可能是白做一场,同时让代码多出了过度的复杂性。

试想一下,如果只是实现一个预计日均PV为10w的书本管理系统,我们用的不是nginx+php+mysql,而是负载均衡+分布式soa+分布式队列+分布式cache+nosql,嗯,这有点6。估计写完代码了,书本管理系统的需求都已经推倒重做无数遍了。

更好的办法是保证架构在最简单的复杂度下刚好能用即可。在实在遇到更为复杂的需求时,为了实现这个需求,实现的复杂性比重构代码还高时,我们才驱动架构的变化。让需求驱动架构的发展,而不是臆想架构的发展。

好架构是迭代出来的,不是设计出来的。

  • 过度设计
  • 过早优化
  • 过度测试
  • 过度文档

重构

4.2 奥卡姆剃刀定律

如果两个设计实现后的效果是相差不大的,那么就选择最简单的那个设计。这启发我们,接口数量仅可能少,接口参数尽可能少,数据字段也尽可能的少。

4 Design for failure

错误是每时每刻都会发生的,必须要检查。如果发生错误时,必须中断操作,不往下继续,避免发生严重的数据不一致问题。

4.1 为什么错误是普遍的

假设,我们系统依赖有15个外部系统,每个外部系统的每天正常运行率是99%,也就是大概99天崩溃一次的概率。那么我们系统每天碰到的外部系统正常运行率为

99%^15=86%

而一个合格的系统,每天的正常运行率应该是99%以上,但由于依赖了15个外部系统,整体正常运行率就会被直接拖低到86%。

另外,前端输入参数传入的是contentId是个undefined数据,用户未登录却企图点赞,数据库数据被意外串改为不合法的值,这些事情每天都在发生。

错误是普遍的

4.2 检查错误

发生错误不是问题,问题是发生错误时,如果我们逻辑还继续进行,这个错误就会不断扩散到整个系统上,这时候很大一部分的数据都不一致了,想修复数据只能回滚了昨天了。所以,错误必须检查,如果发生了就必须中断逻辑。而软件中,因为错误而中断逻辑,它的名字叫异常。

//输入
data := struct {
    Url     string
    Plaform string
}{}
this.CheckGet(&data)

输入要做校验

//检查权限
this.UserLoginAo.CheckMustLogin()

登录态要做校验,而不是取传入的userId

sess, err := this.Session.SessionStart()
if err != nil {
    panic("session启动失败")
}
defer sess.SessionRelease()

外部依赖的redis,db操作失败时要panic

4.3 监控错误

当系统性错误出现时,除了中断逻辑外,接口返回500错误外,框架还需要自动打日志,记录问题发生的堆栈信息,记录错误的现场信息,并将问题以短信或邮件的方式及时反馈给开发者。方便开发者及时发现问题与修复问题。

而业务性错误出现时,则简单地返回异常信息即可,不需要报警。

4.4 容纳错误

还记得,过年时微信抢红包,由于流量巨大,微信的朋友圈看视频都会提示“暂不能使用微信视频”么。由于红包是过年抢红包主要的业务,视频占有的资源很多,但相对红包并不重要。所以,通过优雅降级部分业务,来保证主要业务的顺利运行。

例如,用户在查看食谱信息时,有两个步骤

  • 获取食谱信息
  • 记录食谱的浏览数

很明显,“获取食谱信息”是主逻辑,“记录食谱的浏览数”是旁路逻辑。如果“记录食谱的浏览数”这一步崩溃了,我们系统仍然可以安全地对外服务,返回200返回码以及食谱信息,而不是整个接口一起返回500了,只是后台中的记录数据变得不太可靠而已。容纳错误,优雅降级,能让系统变得更可靠更健壮。

//原代码
func (this *RecipeController) Get_Json()interface{}{
    this.RecipeAo.Visit(contentId)
    
    return this.RecipeAo.Get(contentId)
}

//容错代码
func (this *RecipeController) Get_Json()interface{}{
    this.RecipeAo.Visit_WithException(contentId)
    
    return this.RecipeAo.Get(contentId)
}

代码更改如上

相关文章