微服务SpringCloud实践

Spring Cloud

SpringCloud是Netflix和Spring合作开源的一整套微服务套件的总称,在微服务刚兴起的时候SpringCloud约等于微服务代名词,知道进来阿里也杀入微服务开源领域,开源了SpringCloudAlibaba。

服务治理:Spring Cloud Eureka

他是基于Netflix Eureka做了二次封装,主要做服务的自动化注册与发现。支持集群去中心化部署。

客户端负载均衡: Spring Cloud Ribbon

传统的负载均衡如F5,A10硬件负载或者 Nginx软件负载都是基于服务端的负载均衡。Ribbon提供了客户端负载均衡,正是基于服务发现的基础可以在调用方client中直接配置相关的负载策略,比如均衡等。

分享到

重构的原则

什么是重构?

  • 重构(名词):对软件内部结构的一种调整,目的是在不改变软件可观察行为的前提下,提高其可理解性,降低其修改成本。
  • 重构(动词):使用一系列的重构手法,在不改变软件可观察行为的前提下,调整其结构。
  • 重构的关键:在于运用大量微小且保持软件行为的步骤,一步步达成大规模的修改,每个单独的重构要么很小,要么由若干小步骤组合而成。因此,在重构的过程中,我们的代码很少进入不可工作状态,即使重构没有完成,我也可以在任何时刻立刻停下来。

为什么要重构?

重构改进软件的设计

  • 如果没有重构,程序的内部设计(或者架构)会逐渐腐败变质。当人们只为短期目的而修改代码时,他们经常没有完全理解架构的整体设计,于是代码逐渐失去自己的结构。
  • 程序员越来越难以通过阅读源码来理解设计。代码结构的流失有积累效应。越难看出代码所代表的设计意图,就越难保护其设计,于是设计就腐败的越快。经常性的重构有助于代码维持自己该有的形态。
  • 消除重复的代码是优秀设计的根本。

    重构使软件更容易理解

  • 程序设计的根本是准确的告诉计算机我要做什么,但是程序的另外一个潜在消费者是几个月之后的另一个程序员,如果代码不易理解,那么那个“另一个程序员”原本可能一小时的代码修改,可能需要一周,即使这个“另外的程序员”就是原来的代码开发者,也有可能会“不认识”自己曾经写过的代码,甚至抱怨以前自己写的这么烂。。

    重构帮助找到bug

  • 如果对代码进行重构,我就可以深入理解代码的所作所为,并立即把新的理解反映到代码当中。搞清楚程序结构的同事,也验证了自己所做的一些假设,于是想不把bug揪出来都很难。

    重构提高编程的速度

  • 当需要添加新功能时,内部质量好的软件,让我可以很容易找到在哪里修改,如何修改。良好的模块划分,使我只需要理解代码库的一小部分,就可以做出修改,如果代码很清晰,我引入bug的可能性就会很小,及时引入bug,调试也会容易很多。通过投入精力改善内部设计,可以增加软件的“耐久性”,从而可以更长时间的保持开发的速度。

    何时重构

    在我编程的每个小时里,我都会做重构。

    预备性重构:让添加新功能更容易

  • 重构的最佳时机,就是在添加新功能之前。书中举的一个通俗的例子是需求要你往东100公里,我们不应该直接就往东100公里,而是先看看地图,发现往被20公里上高速,然后往东开100公里速度会快上三倍,这里也讲了需要消除重复的代码,避免一个问题修改好几个地方。

    帮助理解的重构:是代码更易懂

  • 我需要先理解代码子在做什么,然后才能着手去修改。这个书中举例是通过修改一些函数或者变量的命名来增加理解,这个与上面的消除重复代码都是《clean code》里面的要求。

    捡垃圾式重构

  • 在时间和精力允许的情况下,见到一个改一个,像“捡垃圾”一样,积少成多。

    有计划的重构和见机行事的重构

  • 前三种都是见机行事的重构。马丁跟建议尽量不要有计划的重构,除非真的很不容易添加新功能,而几个星期的重构在未来几个月能发挥出很大的价值,才做计划重构。否则日常见机行事的重构是一个好的选择。

    长期重构

  • 不建议让一支团队专门做重构,建议还是每次小改动让整个系统仍然正常工作。如果替换组件的这种,建议引入一层新的抽象,使其兼容两个库,一旦调用方完全改为这层抽象,替换会很容易,这个策略叫做(Branch By Abstraction【mf-bba】)

    Code-Review时重构

  • code review 有助于在开发团队中传播知识也有助于让有经验的开发者把知识传递给比较欠缺经验的人,并帮助更多人理解大型软件系统中的更多部分。我的代码对我来说可能是清晰的,当时对于他人则不然,这是无法避免的,开发者很难为那些不熟悉自己所作所为的人着想。code review可以让更多人提出有用的建议和点子,也更有助于重构。code review的形式有可能是群体review也有可能就是pr的形式,笔者建议与原作者坐在一起看,有助于了解上线文和原作者的意图。

    何时不该重构

  • 如果重写比重构还容易的话。。思考下新核心重写比重构更合适么?个人觉得要从投入产出价值入手去思考。

重构的挑战

重构的唯一目的:就是让我们开发更快,用更少的工作量创造更大的价值。
重构应该总是由经济利益驱动。

代码的所有权

  • 这个讲的是对于接口的重构应当谨慎,外部的调用者可能还在用,更多的建议是接口版本的控制,通过增加新的接口,并将老接口标为(deprecated)逐步淘汰。

    分支管理

  • 马丁建议大家使用主干分支持续集成CI,这个有助于重构,减少了feature分支的合并难度,尤其是较长时间的feature分支的合并,劣势也很明显,对于一些功能的回滚非常不利,个人感觉这个要看实际开发的情况吧,是否适合于拆分更小的功能,来配合CI快速集成。

    测试

  • 不会改变程序可观察行为,这个是重构的一个重要特征。
    需要有自测试代码,这样能够降低重构带来的风险,快速的发现问题,自测试代码也是极限编程的另一个重要组成部分,也是持续交付的关键环节。

    遗留的代码

  • 遗产对于大多数人来说是好事,不过对于程序员,那就呵呵🙄了,甚至是瑟瑟发抖(别人的代码)。
    笔者的建议还是一点点修改,不要急于求成。

    数据库

  • 数据库的重构也建议循序渐进,小修改,或者渐进式修改,比如修改某个字段名,先增加一个字段,然后同时写入新旧两个字段,然后数据迁移,之后改为读新字段,不在写入旧字段,笔者建议读者两本书《渐进式数据库设计【mf-evodb】,和 数据库重构【Ambler&Sadalage】
分享到

微服务设计

微服务与SOA的区别

微服务与 SOA 到底有什么区别,各自的应用场景是什么?

  • 到底在什么样的情况才适合使用微服务架构?
    如果一句话来谈SOA和微服务的区别,即微服务不再强调传统SOA架构里面比较重的ESB企业服务总线,同时SOA的思想进入到单个业务系统内部实现真正的组件化,微服务是SOA的一种轻量级的解决方案,其本质还是SOA,只是更容易落地而以。微服务架构强调的第一个重点就是业务系统需要彻底的组件化和服务化,原有的单个业务系统会拆分为多个可以独立开发,设计,运行和运维的小应用。这些小应用之间通过服务完成交互和集成。每个小应用从前端webui,到控制层,逻辑层,数据库访问,数据库都完全是独立的一套。

微服务的特点

  • 技术的异构性:各个微服务的技术栈从前到后可以自成一体
  • 弹性:某个服务出现故障,会极小的影响其他服务,相对于SOA一个服务处故障可能整个SOA不可用,微服务影响范围小,且可以通过熔断,限流,降级等策略来极小化影响范围,并且由于服务的微小可以快速解决快速部署。
  • 扩展:庞大的服务只能作为一个整体进行扩展,微服务可以从很小的角度去扩展,从而节约成本和时间。
  • 简化部署:部署的频次更高,还可以结合灰度部署降低线上事故风险
  • 与组织结构相匹配:一个小的团队负责一个微服务或者几个微服务的全生命周期
  • 可组合性:面对外部不同的调用者如web,mobile等,微服务可以组合式的提供服务
  • 可替代性:对于某个微服务可以快速重写替换。

微服务架构的不足

  • 微服务把原有的项目拆分成多个独立工程,增加了代码管理、集成测试、运维、监控等的复杂度。
  • 微服务架构需要保证不同服务之间的数据一致性,引入了分布式事务和异步补偿机制,为设计和开发带来一定挑战。
分享到

注释

注释

最好的代码不需要注释,如果一定要有注释最好只有下面几种吧

  • 法律信息:Copyright
  • TODO注释
  • 公共的API中的Javadoc
  • 类文件头的作者和时间信息
分享到

对象和数据结构

注释

  • 数据的抽象应该是提供接口而非实现
  • 得墨忒耳律:模块不应了解他操作对象的内部情形,只暴露操作不暴露内部数据结构。
  • 书中的数据结构一般指的是诸如DTO这种类型。
分享到

边界和测试

边界

  • 文中建议尽量不要再边界中使用map,个人建议如果使用DDD设计的话,在边界增加防腐层,与领域层很好的隔离,能够降低被腐蚀的可能性。

单元测试

TDD三定律

  • 在编写不能通过的单元测试前,不可编写生产代码
  • 只可编写刚好无法通过的单元测试,不能编译也算不通过
  • 只可编写刚好足以通过当前失败测试的生产代码

保持测试整洁

  • 测试代码和生产代码一样重要,需要被设计和思考
  • 测试代码最重要的是可读性
  • 每个测试一个断言

    FIRST原则

  • 快速(Fast):测试应该够快
  • 独立(Independent): 测试应该相互独立
  • 可重复(Repeatable):测试应该可重复
  • 自足验证(Self-Validating):测试应该有布尔值输出。
  • 及时(Timely) 测试应该及时编写
分享到

错误处理

错误处理

  • 使用异常而非返回码,这个在之前的章节中已经提起了
  • 打包外部api的异常
  • 别返回null,可以返回空对象或者异常
  • 别传递null,可以报一个参数检验异常
  • 何为受检异常何为非受检异常?
分享到

有意义的命名

有意义的命名

有意义的命名能够增加代码的可读性,使得维护代码变得容易,也让重构变得更加高效。

  • 名副其实:不要用简单语,使用合适的名称
  • 避免误导:1和l(L)还有一些近义词的误导要避免,0和o(O)
  • 做有意义的区分:避免使用a1,a2这种
  • 使用读得出来的名称
  • 使用可搜索的名称
  • 避免使用编码
  • 避免思维映射
  • 类名:用名词
  • 方法名:动词或者动词短语
  • 别扮可爱
  • 每个概念对应一个词
  • 别用双关语
  • 使用解决方案领域名称
  • 使用源自所涉及问题的领域名称
  • 添加有意义的语境
  • 不要添加没用的语境
分享到

格式

注释

  • 垂直格式要注意缩进
  • 水平格式要注意一行代码的最大长度,建议45个字符左右。尽量保持代码短小。
  • 不建议水平对齐
  • 缩进:方法对于类缩进一级,代码块对于方法缩进一级
  • 当自己的规则与团队冲突时以团队规则为准
分享到

类和系统

封装

类应该短小

  • 单一权责原则 SRP:系统应该由许多短小的类而不是少量巨大的类组成。每个小类封装一个权责,只有一个修改原因,并与少数其他类一起协同达成期望的系统行为。
  • 内聚:类应该只有少量的实体变量,类中的每个方法都应该操作一个或多个这种变量。当类失去了内聚性,就应该拆分它。将大函数拆成许多小函数往往也是将类拆分成多个小类的时机。
  • OCP开闭原则:类应该对扩展开放,对修改封闭
  • 隔离修改:需求会改变,所以代码会改变。
  • DIP依赖倒置原则:类应该依赖于抽象而不依赖于具体细节。
    ##系统
  • 依赖注入 IOC DI
  • 面向切面编程: AOP
分享到