你要如何衡量你的人生

坚持,努力,让好事发生

负载均衡笔记

负载均衡是什么

负载均衡主要适用于如下场景:
流量分发,将高访问量的业务通过负载均衡分发到后端服务器上。
消除单点故障,当其中一部后端服务器不可用时,负载均衡可自动屏蔽故障的 后端服务器,保障应用系统正常工作。
横向扩展,根据业务发展的需要,按需扩展应用系统的服务能力。

负载均衡解决了什么问题

(1) 负载均衡算法

常见的几种负载均衡算法: 轮询法、随机法、源地址哈希法、加权轮询法、加权随机法、最小连接数法

LVS负载均衡十种算法

阅读全文 »

 在我们要新增功能或者提高代码扩展性的时候可能需要用到代理。
 比如在不改变已有代码的情况下,需要增加统计每个方法耗时的功能,怎么解决?

 其中一个办法就是通过代理实现。常用的有静态代理和动态代理。  

(1) 静态代理

 静态代理其实就是设计模式里的代理模式。

 代理模式是一种设计模式,提供了对目标对象额外的访问方式,即通过代理对象访问目标对象,这样可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
代理模式  

阅读全文 »

 注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。

阅读全文 »

 SPI(Service Provider Interface),是JDK内置的一种 服务提供发现机制,主要作用是解耦,提高可扩展性。

(1) SPI是什么

 SPI(Service Provider Interface),是JDK内置的一种 服务提供发现机制,可以用来启用框架扩展和替换组件,主要是被框架的开发人员使用。

 比如java.sql.Driver接口,其他不同厂商可以针对同一接口做出不同的实现,MySQL、PostgreSQL、Oracle都有不同的实现提供给用户,而Java的SPI机制可以为某个接口寻找服务实现。

 Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是 解耦。

SPI整体机制图

Java SPI流程

阅读全文 »

面向对象的三大特性:封装、继承、多态。

那么多态是什么,有什么优点,又是怎么实现的呢?

(1) 什么是多态

多态是同一个行为具有多个不同表现形式或形态的能力。

比如:猫、狗 都是动物,但是在这个行为上,表现不一样,猫叫起来是,狗叫起来是

class Animal {
    public void say() {
        System.out.println("叫");
    }
}

class Dog extends Animal {
    public void say() {
        System.out.println("汪汪");
    }
}

class Cat extends Animal {
    public void say() {
        System.out.println("喵喵");
    }
}
public static void main(String[] args) {
    Animal animal = new Dog();
    animal.say();
    Animal animal2 = new Cat();
    animal2.say();
}
阅读全文 »

TCC是什么

TCC 是一种补偿型事务,该模型要求应用的每个服务提供 try、confirm、cancel 三个接口,它的核心思想是通过对资源的预留(提供中间态,如账户状态、冻结金额等),尽早释放对资源的加锁,如果事务可以提交,则完成对预留资源的确认,如果事务要回滚,则释放预留的资源。

组成

TCC模型完全交由业务实现,每个子业务都需要实现Try-Confirm-Cancel三个接口,对业务侵入大,资源锁定交由业务方。

1、Try:尝试执行业务,完成所有业务检查(一致性),预留必要的业务资源(准隔离性)。
2、Confirm:确认执行业务,不再做业务检查。只使用Try阶段预留的业务资源,Confirm操作满足幂等性。
3、Cancel:取消执行业务释放Try阶段预留业务资源。

一个完整的业务活动由一个主业务服务与若干子业务服务组成:
1、主业务服务负责发起并完成整个业务活动
2、业务服务提供TCC型业务操作。
3、业务活动管理器控制业务活动的一致性,它登记业务活动中的操作,并在业务活动提交时确认所有的TCC型操作的Confirm操作,在业务活动取消时调用所有TCC型操作的Cancel操作。

成本

1、 实现TCC操作的成本
2、业务活动结束时Confirm或Cancel操作的执行成本。在Confirm和Cancel范围内的操作成功性需要框架来保证,只能一直重试保证资源被消耗或者释放。
3、业务活动日志成本

适用范围

1、强隔离性、严格一致性要求的业务活动
2、适用于执行时间较短的业务

应用案例

下单扣减资源

下单扣减库存

转账

A用户给B用户转账

TCC与2PC对比

TCC将事务提交划分成两个阶段,Try即为一阶段,Confirm 和 Cancel 是二阶段并行的两个分支,二选一。从阶段划分上非常像2PC,我们是否可以说TCC是一种2PC或者2PC变种呢?其实不可以,原因如下:
1、2PC的操作对象在于资源层,对于开发人员无感知;而TCC的操作在于业务层,具有较高开发成本。
2、2PC是一个整体的长事务,也是刚性事务;而TCC是一组的本地短事务,是柔性事务。
3、2PC的Prepare(表决阶段)进行了操作表决;而TCC的try并没有表决准备,直接兼备资源操作与准备能力
4、2PC是全局锁定资源,所有参与者阻塞 交互等待TM通知;而TCC的资源锁定在于Try操作,业务方可以灵活选择业务资源的锁定粒度。

TCC注意事项

TCC为了解决网络不可靠引起的异常情况,要求业务方在设计上要遵循三个策略:
1、允许空回滚:原因是异常发生在阶段一时,部分参与方没有收到 Try 请求从而触发整个事务的Cancel 操作;Try 失败或者没有执行 Try 操作的参与方收到 Cancel 请求时,要进行空回滚操作。
2、保持幂等性:原因是异常发生在阶段二时,比如网络超时,则会重复调用参与方的 Confirm/Cancel 方法,因此Confirm/Cancel方法必须保证幂等性。
3、防止资源悬挂:原因网络异常导致两个阶段无法保证严格的顺序执行,出现参与方侧 Try 请求比 Cancel 请求更晚到达的情况,Cancel 会执行空回滚而确保事务的正确性,但是此时 Try 方法也不可以再被执行。

TCC对业务的强侵入性,使用成本非常昂贵,虽然提供了更灵活的资源锁粒度,对标2PC拥有更高的吞吐量。

参考

[1] 我说分布式事务之TCC
[2] 分布式柔性事务的TCC方案

(1) DDD是什么

领域驱动设计(Domain-Driven Design,简称DDD)

(2) 为什么使用DDD

防腐层,该层负责与外部服务提供方打交道,还负责将外部概念翻译成自己的核心领域能够理解的概念。

(3) 怎么做

(3.1) DDD战略战术

DDD的战略设计主要包括领域/子域、通用语言、限界上下文和架构风格等概念。 

(3.1.1) 战略

DDD的战略设计主要包括领域/子域、通用语言、限界上下文和架构风格等概念。 

领域

在日常开发中,我们通常会将一个大型的软件系统拆分成若干个子系统。
在DDD中,我们对系统的划分是基于领域的,也即是基于业务的。 

统一语言

DDD引入了统一语言,把业务名词含义事先确定好,减少不必要的翻译过程,车同轨,书同文,行同伦

这也消除了业务与技术之间的重复,共同使用业务原语对话,代码就是文档,代码就是领域知识

userService.love(Jack, Rose) => Jack.love(Rose) 

界限上下文

引入限界上下文的目的,不在于如何划分边界,而在于如何控制边界
限界上下文是“分而治之”架构原则的体现,我们引入它的目的其实为了控制(应对)软件的复杂度
是对领域模型、团队合作以及技术风险的控制 

限界上下文和上下文映射图

限界上下文和领域具有一对一的关系。

当限界上下文作为领域模型的边界时,一方面它限制了跨限界上下文之间领域模型的关系,另一方面它作为知识语境,分离了同一个领域概念的不同视角。我将限界上下文称为战略设计的基本架构单元。

架构风格

DDD并不要求采用特定的架构风格,因为它是对架构中立的。
你可以采用传统的三层式架构,也可以采用REST架构和事件驱动架构等。
在《实现领域驱动设计》中,作者比较推崇事件驱动架构和六边形(Hexagonal)架构。

(3.1.2) 战术

战略设计为我们提供一种高层视野来审视我们的软件系统,而战术设计则将战略设计进行具体化和细节化,它主要关注的是技术层面的实施,也是对我们程序员来得最实在的地方。
对于开发人员而,战术是最实用的,比如聚合、实体、值对象、工厂、仓储、领域事件等等。

自上而下的结构化分解 + 自下而上的面向对象建模,过程化分析更好地清理了模型之间的关系,而对象模型提升代码复用性和业务语义表达能力。

行为饱满的领域对象

要创建行为饱满的领域对象并不难,我们需要转变一下思维,将领域对象当做是服务的提供方,而不是数据容器,多思考一个领域对象能够提供哪些行为,而不是数据。

实体vs值对象

实体表示那些具有生命周期并且会在其生命周期中发生改变的东西;
而值对象则表示起描述性作用的并且可以相互替换的概念。

聚合(Aggregate)

资源库(Repository)
资源库用于保存和获取聚合对象,在这一点上,资源库与DAO多少有些相似之处

DAO只是对数据库的一层很薄的封装,而资源库则更加具有领域特征。
所有的实体都可以有相应的DAO,但并不是所有的实体都有资源库,只有聚合才有相应的资源库。

参考资料

[1] 美团技术沙龙:基于领域驱动设计(DDD)的架构演进和实践
[2] 阿里技术专家详解DDD系列 第五讲:聊聊如何避免写流水账代码
[3] 会前答疑 | 来自听众的16个DDD问题,美团技术团队是这样回答的

[1] DDD之战略战术设计
[2] DDD 领域驱动设计落地实践系列:战略设计和战术设计
[3] 领域驱动设计在马蜂窝优惠中心重构中的实践
[4] 如何分辨应用服务与领域服务
[5] 领域驱动建模与面向对象建模的差异
[6] 端口和适配器架构——DDD好帮手
[7] 有了MVC,为什么还要DDD?

1.代码逻辑 以及 层次做有效拆分
2.交易流程相互隔离;
3.新人对代码理解更容易;

原有代码结构逻辑基本为瀑布式编程,service从头写到尾,没有做很好的抽象;
很多if else,改一个业务场景,容易对另一种业务场景产生影响。

使用Lua脚本的好处:

减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延。
原子操作。redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。因此在编写脚本的过程中无需担心会出现竞态条件,无需使用事务。
复用。客户端发送的脚本会永久存在redis中,这样,其他客户端可以复用这一脚本而不需要使用代码完成相同的逻辑。

EVAL script numkeys key [key ...] arg [arg ...]

说明:
script是第一个参数,为Lua 5.1脚本。该脚本不需要定义Lua函数(也不应该)。
numkeys指定后续参数有几个key。
key [key ...],是要操作的键,可以指定多个,在lua脚本中通过KEYS[1], KEYS[2]获取
arg [arg ...],参数,在lua脚本中通过ARGV[1], ARGV[2]获取。

127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 value1 value2
1) "key1"
2) "key2"
3) "value1"
4) "value2"
127.0.0.1:6379>
127.0.0.1:6379> eval "redis.call('SET', KEYS[1], ARGV[1]);redis.call('EXPIRE', KEYS[1], ARGV[2]); return 1;" 1 key1 value1 60
(integer) 1
127.0.0.1:6379> get key1
"value1"
127.0.0.1:6379> ttl key1
(integer) 53
127.0.0.1:6379>

这个脚本相当于执行了2条命令 SET key1 value1 EXPIRE key1 60

参考资料

[1] Redis使用lua脚本
[2] EVAL script numkeys key arg
[3] EVAL
[4] Scripting with Lua

0%