首页 > 经验记录

 

为了解决在分布式系统中需要对某个资源进行全局的一个非重复ID生成,所以有了分布式ID这么一个概念

 

在分布式应用下,像分库分表的这种场景是很常见的, 这个时候如果还是用数据库本身的自增的话,那多个数据库ID肯定会重复。

比如订单表由于数据过多,分到了多个数据库中存储的话,那么这个ID要如何生成呢?

还是以原来的逻辑进行自增的话,那就会出现这种情况:   数据库A里边有订单1、2、3 , 数据库B里边也有个订单1、2、3, 这在业务逻辑上就冲突了。

 

 

那么这就需要有一个分布式ID生成系统

而一个分布式ID系统需要满足的有这么几点:

1、高可用, 所有的业务系统都需要调用这么一种id生成器来生成id,这玩意千万不能挂

2、趋势有序, 无论是啥关系型数据库,在一个字段上加索引肯定是有序的更快

 

 

解决方案:

 

1、UUID

这个建议不用管,虽然好处多(无脑)。但是代价太大了。

 

主要就是在DB里边用UUID太长了,空间又占得大,你建的索引越多, 影响越严重。

UUID还是非自增,无论是增删改查相对于自增id的比较大小来说都要慢很多,尤其是在那种取数据段的场景。

 

 

2、数据库ID自增

那为了保证高可用,一个DB肯定是不行的,那必须得集群才行。

就用mysql举例:

主从:  那肯定是不行的。 slave同步master的数据会出现延时,在master挂掉时可能出现获取ID重复的情况

主主:  如果只是普通的使用自增策略,那这个也会有问题。假设有A/B两个数据库,都是1、2、3自增。那么每个ID都会是重复的。

 

这个时候就要引入一个步长的概念。假设设置MySQL自增策略步长为2。然后其中一个从1开始,另一个从2开始。那么A/B两个数据库自增的ID就会是1、3、5/2、4、6  ; 这样子就很美好了。

可以用这个语句设置, 多个库都用不用的起始值和相同的步长就行了。重要的是有多少个DB就要有多长的步长

 

 

可是这样也存在一个问题,那就是无法扩容

用多主数据库来进行ID自增的话,只要设定好了,就再也无法变更了。以我上边的来说, 想要原基础上添加一个C数据库来进行ID自增,那是做不到的。

这样子就丧失掉了拓展性。虽说在一般的企业应用中这样子一个架构也确实够了 。

 

当然,每次生成ID都去走数据库也怪麻烦的,所以一般也有个ID生成服务,各个应用只需要请求那一个服务就行了。由那个服务去获取ID,达到一个解耦的目的。

这个服务为了高可用也要集群才行,不过这个集群只是为了防特殊情况导致服务挂掉 ,  他们请求的数据源都是一样的。

 

既然可以用MySQL这种关系型数据库,那肯定也可以用Redis这种基于内存的NoSQL数据库来实现这个。

那这个效率不是比关系型数据库高不少?

使用Redis的INCRBY命令就可以从容的在值上增加指定的数字,设置起始值、自增步长。Redis也可以很轻松做到嘛。

但是用Redis的话有一个点要注意:

分布式ID首先得考虑高可用,所以得考虑持久化的问题。Redis支持RDB和AOF两种持久化的方式。

而我们只能用AOF,对每一条写命令进行持久化,因为RDB持久化相当于定时打快照来持久化,如果打完快照后,还生成了多次ID,没来得及做下一次快照的时候Redis挂了,重启Redis后就会出现ID重复。

 

3、号段

其实号段也算是数据库ID自增的一种形式。这里只是单独拿出来说

不过就是将原本的一个个获取改成了批量获取,比如直接获取这个数据库里边1 – 10000的ID (只要查出一个数据段落和步长就够了, 自增在自己服务里边自增)。存在分布式ID服务的缓存里边。

这样就免得一次次去和数据库交互。

而且这种方案不再强依赖数据库,就算数据库不可用,那么这个分布式ID服务也能继续支撑一段时间。缺点就是他挂掉重启的话,会丢失一段ID。

之前说过,分布式ID服务的数据源都是一样的。而正是因为数据源一样, 在号段模式里边可能出现那种两个ID服务同时请求一个数据库的同一段数据的情况。那这就很麻烦。

所以这个号段模式需要上一个乐观锁才可以,关于乐观锁可以看我这篇文章:  [浅谈CAS与乐观锁]  http://skypyb.com/2019/08/jishu/961/

 

当然,这种轮子就别自己实现了,人家早就给你安排的明明白白的,只管拿过来用就OK

比如滴滴开源的TinyId:   https://github.com/didi/tinyid

 

4、SnowFlake

这个就是分布式ID界的头头, 著名的雪花算法

对,他是一种算法,是twitter开源的分布式ID生成算法,他和我上边说的数据库生成分布式ID机制完全不一样,最核心的就是它不依赖数据库。

不依赖数据库,这也太美妙了。要知道在软件中每引入一个额外变量都会使架构的复杂度几何上升,SnowFlake无疑就是那分布式ID的最佳实现者。

核心思想是:

分布式ID固定是一个long型的数字,一个long型占8个字节,也就是64个bit,那这64个bit的每一位分配如下: 

 

第一个bit位是标识,在java中由于long的最高位是符号位,正数是0,负数是1,由于一般生成的ID为正数,所以固定为0。

时间戳部分占41bit,这个是毫秒级的时间,这个会储存时间戳的差值(当前时间-固定的开始时间),因为这样可以使产生的ID从更小值开始;41位的时间戳可以使用69年,(1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69年

工作机器id占10bit,这里可以很灵活,比如,可以使用前5位作为数据中心机房标识,后5位作为单机房机器标识,可以部署1024个节点。

序列号部分占12bit,支持同一毫秒内同一个节点可以生成4096个ID

 

根据这个算法的逻辑,只需要将这个算法实现出来,封装为一个工具方法,那么各个业务应用可以直接使用他来获取分布式ID,只需保证每个业务应用有自己的工作机器id即可。直接降低大量复杂度。

关于这个算法的具体实现,搜索引擎搜索SnowFlake,一堆实现。直接复制到自己的项目里边用就完事了。

 

关于雪花算法,其实也有一个小缺点。那就是要为每个机器指定一个机器id, 几十台几百台机子还好,更多那就很操蛋了。

所以有大厂对SnowFlake进行了改造,让其可以自动分配。比如百度的 uid-generator:  https://github.com/baidu/uid-generator

 

结语: 

分布式ID这玩意,其实是个非常简单的东西。

我相信任何一个有点分布式经验的开发者都可以在短时间内理解并在生产中实践。

我就想说一句: 其实用哪个都无所谓。

你要是真的业务有什么东西每天几亿个ID自增,估摸着公司也是流水多多了,轮不到普通程序员操心这玩意。 要是就一普通软件,用户几万几十万的。搞两台Redis来incr也是一样的…

阅读全文

说完了分布式事务最核心的思想(2PC) –> [探秘分布式解决方案: 分布式事务——从核心思想之2PC(两阶段提交)开始]    http://skypyb.com/2019/11/jishu/1149/

 

那么现在进入到更加复杂的场景。像这种跨库调用之类的,一线互联网公司早就不玩这一套东西了。这都9102年了,上来就是微服务架构。

我这么多服务,你整个啥跨库调用呢?一个服务可能同时调用多个其他的服务。这多个其他的服务中都要执行SQL语句,修改落实到服务所对应的数据库之中。

 

 

 

微服务下的服务调用

还是以一个订单支付的流程为例,只不过由之前讲解2PC的单机多库示例改为了微服务示例。

微服务架构下,用户下单的流程将会变成这个样子:

微服务下的订单服务

 

这里边每一个 “服务” 都是一个系统,都是会单独部署的。

可以见得这个复杂度上升的非常大。比如说我用户在下单购买,调用了订单服务的接口,此时订单接口的方法内逻辑不单单只是订单服务自己操作自己的数据库。

订单还得去调用其他的服务的接口。比如库存、积分、仓储 等。

 

那么,在微服务架构下,服务之间互相通过接口进行通信,如何才能在一个 “事务” 中保证数据一致性?

如果出现这种情况   比如订单支付状态已经修改完毕、库存扣除完毕,但是加分却失败了, 这样子咋回滚?

 

那么就轮到新的解决方案出场了。那就是 TCC (Try-Confirm-Cancel)

TCC全名为 两阶段补偿型方案。是现在的互联网大厂主流的分布式事务解决方案之一。而TCC,就是脱胎于2PC,是作为2PC思想在微服务领域的子集被使用着。

所以说为啥2PC是核心思想,不懂2PC,那TCC是肯定明白不了的。 要是懂了TCC的原理,那么前置条件必然是已经懂了2PC。

 

 

 

TCC原理思想

而TCC的原理相比于2PC,有这样一种行为上的变更:

2PC的行为是:  1、预提交 2、实际提交

TCC将其改为了: 1、预留资源 2、确认资源

 

什么叫做预留资源?什么叫做确认资源?

还是以上面我画的的订单活动图为例子,预留的资源,那就是 订单状态、库存数、积分数、出库单

那我们在TCC的方案下进行事物的话,第一阶段就是将这几个资源给他锁定住。然后在第二阶段进行一个资源的确认,即提交/回滚

你如果知道2PC的思想,我想这完全不难以理解。下面是TCC方案的状态图:

TCC-STATE

 

在TCC方案下,在第一阶段中的修改,修改的并不是实际期望的结果,而是期望结果之前的一个中间值。

所以,每个服务都要提供两个接口,一个用于第一阶段的资源预留行为,另一个用于确认/回滚。

(其实你也可以就搞一个接口,然后参数传个对象,由对象中的属性来决定做什么操作也不是不行)

 

比如更改订单,我不可能一下就将这个订单给改成已支付。毕竟现在这个方法还跑到一半,不能完全确定之后的业务是否正常进行。

所以,在此取了个中间态,将订单改为 “更新中” 。

库存、积分等服务也和订单相同。 当所有的接口均调用完成后,即所有的资源预留操作均完成。则在这时进行确认行为。

如果说所有的操作都返回了成功,那么就将订单状态改为已支付,然后将冻结的库存真正减掉、积分真正加上、草稿出库单改为生效出库单。 (confirm)

而反之,若是其中有任意一个接口失败了,那么就对应执行 calcel 操作,将所有改变的状态通通回滚。

 

 

 

如何预留资源

而有的人可能会好奇,什么冻结库存之类的玩意如何实现?

这个其实也很简单,比如你库存表本来只有一个库存数字段。但你现在要用这种TCC方案了,那么就给他再加上一个冻结库存数字段

本来来说,你某个商品库存数是10 , 那么用户再下单完成支付的这个流程中,假设买了1个商品,那么原来的逻辑就得改一下。

正常你买1个商品,那库存就是10-1=9 咯,所以将库存数改为9。而在TCC下,你就不将他改为9,而是在冻结库存数上+1,即数据库状态变为 库存数10,冻结库存数1

那么在其他的用户购买时,你返回给前端的库存数应该就是 库存数10 减去 冻结库存数1 等于现在只有9个库存能够被使用。

用户他不知道冻结不冻结,但是你的程序里边是知道的。用这种逻辑那是一点没有问题。

在你支付接口里边所有的预留操作成功之后,那么就如上所述,将库存字段真正减掉,也就是将10库存真正减1,将冻结库存还原,这是第二阶段该做的事情。

 

说到这里,总没人问第二阶段要是某环节错了怎么办吧? 要是有这疑问的那肯定是没看我上一篇分布式事务文章。

这里也简单的讲一句:本身失败的概率就极小,要是操作失败了肯定是会重试、并且记录事务日志的。状态一个不对劲就由人肉团队、定时任务来进行补偿。

 

 

TCC讲解完毕,那么到底TCC和2PC有着哪些区别呢?

TCC其实说白了,其实也和两阶段提交差不多。就是要开发新的接口。

毕竟是在微服务架构下,复杂度上升很多,要在这种架构下来进行分布式事务本身就要比单机应用考虑的方面更广。

而TCC和2PC之间最主要还有这么一个区别:

在很复杂的、跨了很多数据库的业务场景下,2PC的锁粒度非常大。因为所有的数据库锁均被应用所持有,直到事务完成。性能会很低,根本顶不住大并发。

而TCC的话,从整个系统上来看,吞吐量会好非常多,因为在TCC方案下的的复杂调用链之中,实际上每个数据库操作,都是commit了的,换而言之就是锁释放了并不是一个加在全局的大锁,而是将锁粒度控制在每一个系统的每一个接口里。嘿,有没有想到ConcurrentHashMap。

 

 

 

说在最后

如果将TCC这种解决方案称之为 “术” 。那么2PC就是所谓的”道”

只要你学会了”道”   那么一切基于此道的术相信都难不倒你了。

当然,像这种TCC的实现肯定是有现成的给你用。毕竟自己来搞这么一个框架也是蛮复杂的,不推荐自个造轮子。

业界开源实现也挺多比如ByteTCC、seata、tcc-transaction等,使用方面就不贴了,基本都是配置。

而除了TCC,其实还有像是最大努力通知型方案可靠消息最终一致性方案这些基于最终一致性的分布式事务解决方案。也可以网上搜搜了解一下。

 

阅读全文

为什么要有分布式事务?

本地单机事务,有点工作经验的肯定不陌生,主要用于处理操作量大,复杂度高的数据,一般都由数据库自己实现。

开启一个事务,进行了多个对数据库进行更新(增删改)的语句后,可以自由的选择 commit rollback 来结束事务。

可以从根源上保证多个数据集合的同步。

 

比如一个订单系统,用户支付订单后,订单表的订单状态被改为已支付,同时,库存表中商品剩余数量减一。

若是没有事务机制,在订单库状态修改成功后,由于库存不够减、网络波动、程序bug、数据库挂掉等原因导致数据库中库存减一的SQL语句执行失败; 那这样到最后就会出现实际库存早就没有了,但用户却支付成功的重大事件,即超卖了。

所幸。只要你开了事物,那么事务的原子性确保让你多个语句作为一个原子操作来执行,要么同时成功要么同时失败,在库存-1 失败的同时你订单修改也失败了。所以在单机简单应用中一般不存在这种问题。

 

可是,在稍微复杂一点的场景中,数据库自带的事务机制也有着无能为力的时候。

仍然是一个订单系统,这回场景为由于系统用户激增,入驻商家也越来越多。在日渐庞大的数据量下出于性能考虑决定对数据库进行拆分,由原来的一个库,拆分为一个订单库、一个库存库

你的程序里边,一个下单方法中会执行两个库的SQL语句,在这样的逻辑下,普通的事务还有用么?

就像这样:

订单服务-1

 

那肯定是没有用的。

这种场景中必须要实现跨库事务才能够满足企业级生产需求。

 

要如何实现这种跨库事务? 这里引出分布式事务中的重点思想:阶段提交

 

两阶段提交协议(2PC)

2PC 引入一个事务管理器来作为事务的协调者。

在程序执行复杂数据操作时,开启事务、SQL语句执行完后,先不进行commit。先将执行的结果告知给事务管理器

这个时候,再执行其他数据库的SQL语句。当这个事务中,所有的SQL语句全部执行完毕后,即事务管理器已经得到所有此次事务中SQL语句的执行结果后。

再集体通知所有的数据库进行commit 或 rollback。

 

以下单流程来模拟的话。

前置条件:已引入事务管理器

此时,用户一个支付请求过来。先执行订单库的修改语句,将用户的订单改为已支付。

执行成功后,不提交事务。再开启库存库的事务、执行库存库的库存修改语句,将库存减一。注意,这是两个不同数据库的不同事务。

这个时候数据库的事务隔离机制就将数据锁住了,包括订单库的订单、库存的数量。但是还没有提交。

在每个SQL执行完成之后,都会将执行完成的信息提交给事务管理器。

那么,当订单、库存都修改成功,事务管理器之中都收到了执行成功信息,确认好了本次事务中的所有语句都执行完成后、立即通知所有数据库进行 commit 操作。

任何一个语句执行失败,事务管理器就会通知所有数据库进行rollback。

 

活动图如下所示:

2PC-01

 

2PC中的底层实现思想,就是将原先的单次提交操作分为两个阶段,即 1、预提交 2、实际提交

看起来是不是很简单。大道至简,实际上只要理解了两阶段提交的思想内核,那么任何分布式事务的解决方案都难不倒你了。

因为TCC、消息事务等事务解决方案,都是从2PC衍生的。

 

 

那么问题来了,2PC凭什么可以解决分布式事务问题?

有人可能也有疑问。2PC好像不是那么严谨?

比如: 事务管理器的通知失败了咋搞,假设事务管理器通知订单库的commit成功了,但是通知库存库去commit的时候失败了。那不还是有问题。

这里就必须说明一下分布式事务解决方案的重点。 那就是: 分布式事务不可能100%解决

不信你去问阿里巴巴的员工、Google的员工,去问他们:”你们公司的分布式事务能不能百分百成功呀?” 肯定不可能,起码现在9102年在这个地球上是没有哪家公司敢说自己的分布式事务是百分百成功的。

虽说分布式事务不可能百分百解决,但是可以尽量提高成功概率,比如让你的成功概率接近99.9999%,即让你的系统尽可能的高可用。

 

 

而2PC就是及其有效的提高成功率的方法

这里要先说明,这个事务管理器并不是一个单独的服务,而是内嵌在你程序里的组件。这玩意挂了你程序也挂了,所以不要操心事务管理器挂掉的问题。

以订单为例,其实最容易出现的问题主要是 库存不够减、数据库挂了、网络波动

而在使用2PC的情况下,你的所有SQL都执行成功了,但是事务管理器commit失败了,是个什么概念啊?

要知道在程序的世界里,你别说分两个阶段,你分再多也就几毫秒的时间。

在下单这里预提交成功后,可以显而易见的得出几个结论:

1、所有SQL都执行成功了,所以数据库在执行SQL语句时状态是正常的、服务器和数据库之间的网络也是正常的

2、由于库存库的SQL执行成功,所以库存够减,不可能出现库存不够的问题

 

你跟我说你预提交完,这个时候事务管理器刚通知完订单库commit成功,在提交库存库减库存commit时数据库挂掉了? 或者网络波动导致通知失败了?明明前1ms还好好的?

wdnmd

咳咳,不否认有这种概率啊。可是对于2PC来说,你通知commit失败后,是会重试的。基本上都不会有问题。

那么,要是一直重试都不成功呢?比如挖掘机在你提交库存库commit时,将网线挖断了。emmmmmm,有这种疑问也实属正常,毕竟啥情况都可能发生是不。

这种情况出现确实没有别的办法。一般来说都会记日志打log,之后要么后台通过定时任务补偿,要么人工介入

像一线互联网公司啊,都有大量的一些人工团队专门处理这种极端情况下出现的数据不一致问题,还会让研发工程师写一些什么脚本之类的去回滚、回复数据。

 

 

综上所述。回过头再品一品两阶段提交的思想。为什么说2PC可以提高分布式事务的成功概率,相信已经了然于心了。

而像是这种2PC的具体实现,那肯定也是不用你自己操心的。在java的世界中这种框架已经有不少了。比如开源框架 atomikos,就可以帮你解决这种跨库事务问题,当然,底层就是用的2PC思想做的。

至于如何具体在代码里实现,这里就不贴了。搜索引擎搜一下atomikos可以搜到一大堆。基本上在Spring里边也就是引入依赖后配置一下,然后编程式事务/声明式事务轻松搞定。还可以找一下、了解一下我这篇博客没说明(因为感觉没什么必要)的XA/JTA 接口规范等。

阅读全文

既然是一个网关。那么全局过滤器肯定是少不了的一个存在。像是鉴权、认证啥的不可能每个服务都做一次,一般都是在网关处就搞定了。

Zuul他就有很强大的过滤器体系来给人使用。

Gateway当然也不会差这么点东西。

对于SpringCloud体系来说,一切的实现都是那么的简单。那么废话不多说,直接开始写起来。

 

Gateway内部有一个接口 名为GlobalFilter,这个就是Gateway的全局过滤器接口,只要在应用中实现此接口后注册为Spring的Bean,背后就会帮你将这个实现注册到全局过滤器链条里边去。

我这里就简单的写了个模拟鉴权的过滤器实现:

虽说是鉴权,但实际上我这就是个简单的demo而已。想知道真正的Spring Security鉴权/认证怎么写?

我以前写的这个: http://skypyb.com/2019/09/jishu/kj/1050/ 应该可以帮助你。

 

看我写的这个过滤器内部实现哈,其实就是拿出Request Header中的 Authorization字段(token) 然后判断是否存在。不存在就返回错误,存在就交给链条中的下一个过滤器。

 

过滤器其实也没啥好说的,那么说说限流。

关于限流这个东西,常见的算法就是漏桶和令牌桶了,对于一个应用单机限流来说也复杂不到哪儿去。

靠着google guava包里的RateLimiter工具都能搞定大多数场景了。

不过既然人家Gateway好心好意给你搞了个限流的实现。那么还是尊重他用一下。

由于Gateway是用的Redis和lua脚本实现了令牌桶的算法,那么先导入几个需要的依赖:

 

既然是Redis,那还是先配一下Redis序列化先:

 

万事俱备,开始进行限流的具体实现了。

 

既然是限流,那么也得有个限流策略

是根据用户来限流呢?还是说根据请求路径限流?或者是IP限流?

不过这个都是由需求来决定了,我这就简单的写个根据IP来限流的。

人家也给你封装完毕了,只需要你自己实现KeyResolver这个接口就可以。

由于实现这个一般来说也就一行代码,所以我就不写个单独的类去实现了,而是直接写在配置类里边。

看,我其实只需要返回我需要限制的东西就可以了。我这里提取到用户的IP将其返回,即可实现通过ip来进行限流的策略。

不过限流相关的配置写了,那也得用起来。

这个怎么用起来? 其实直接在配置文件里配置就OK了

 

这里可以看到,除了Redis配置 ( spring.redis.** )以外。

主要就是对于Getway的配置。

在Gateway路由配置中,设置了一个filters参数。

这个是为了指定路由的各种过滤器的。这个参数也有很多种,可以参考官方讲解: https://cloud.spring.io/spring-cloud-gateway/2.0.x/single/spring-cloud-gateway.html#gateway-route-filters

我这就是指定了一个RequestRateLimiter,请求限流。

redis-rate-limiter.burstCapacity: 20     这个参数表示突发容量,即每秒可以最大通过多少次请求

redis-rate-limiter.replenishRate: 5       这个是令牌桶的补充速度,每秒往桶里边放几个令牌

key-resolver: ‘#{@ipKeyResolver}’             这个就是用上KeyResolver的具体实现了,这里用spel表达式指定我写的那个ip限制类

 

 

准备好之后将应用启动就完事了,想测的话可以用jmeter测测看,或者将请求限制写的更小一点,在网页上狂按f5也行。

 

阅读全文

 

在微服务架构中,网关是必不可少的重要组件。

这关系到了客户端“如何访问”每个服务。

以前主流的方式就是使用Netfilx的Zuul组件。但是,因为某些奇妙的原因,Netfilx全家桶都停止维护了。

我之前也写过Zuul的配置和使用方法:

微服务路由解决方案: “Zuul” 服务搭建;以及自定义Zuul过滤器的代码编写。

Spring Cloud Netflix Zuul 使用自带的 Hystrix 实现回退机制 , 并实现请求在Zuul内部的聚合功能

 

那么,既然Zuul这套东西不维护了,迟早也就是抛弃的份。这时候就到Gateway上场了。

说起Getway的工作流程,大概是这样子的: 客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后最终返回结果。

这么一说…是不是感觉这套流程和 SpringMVC 有点像。这么一看一下子Gateway的流程就感觉铭然于心了有木有。

 

进入正题,开始上手 Spring Cloud Getway 的配置和使用。

当然,路由网关,也是一个项目。

难么必不可少的 项目依赖管理配置文件、应用配置文件、启动类 是肯定需要的。

而他,真的就只需要这几个东西就能完成基本的路由、负载均衡功能。

 

既然要建立项目,pom.xml就是第一个。

Spring Cloud Getway 有一个特别的地方。

那就是他不使用 Web 作为服务器,而是使用 WebFlux 作为服务器。

基于此,那么 spring-boot-starter-web 这依赖就不能上了。

pom.xml完成体如下:

这pom.xml真是又丑又长。我怎么就用了maven没用gradle呢? 不禁陷入了深深的思考

既然是Alibaba体系,那么Sentinel、Nacos等依赖也是需要的。 和之前写过的几个Spring Cloud Alibaba组件的使用结合到一起。

在此之上去掉web依赖,引入 org.springframework.cloud:spring-cloud-starter-gateway 依赖就可以了。

由于过滤器等功能依然需要 Servlet 支持,故这里在依赖一个 javax.servlet:javax.servlet-api

 

 

pom.xml搞定,接下来就是最最最最重要的 application.yml:

 

其他的不说,最主要的是spring.cloud.gateway.route下面的配置。

在这里有几个很重要的点,要重点说一下。

id:  这个是路由ID,值可以随便写,但是一定要保证在整个微服务中此值是唯一的

uri:   lb://是固定写法,表示开启负载均衡。后边跟着的是你注册进nacos的其他服务名字

predicates: 这个是谓词。谓词这个词语一说出来很多人都会蒙圈。但是如果是熟悉JDK1.8的人肯定对这个有印象,Stream API里filter()方法入参就是一个Predicate,返回一个boolean类型的值。虽说Gateway里的和Strean API里的肯定不一样。但是可以这么理解,他是为了判断请求是否满足一个条件而存在的。而我设置的 – Method=GET,POST 表示匹配 GET 和 POST 请求,这俩种类型的请求就可以路由。

 

predicate当然不止Method这一个玩意,他总共有这么些设置:

After | After=2017-01-20T17:42:47.789-07:00[America/Denver]
Before | Before=2017-01-20T17:42:47.789-07:00[America/Denver]
Between | 2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
Cookie | Cookie=chocolate, ch.p
Header | Header=X-Request-Id, \d+
Host | Host=**.somehost.org
Method | Method=GET
Path | Path=/foo/{segment}
Query | Query=baz
RemoteAddr | RemoteAddr=192.168.1.1/24

参考: https://cloud.spring.io/spring-cloud-gateway/2.0.x/single/spring-cloud-gateway.html#gateway-request-predicates-factories

 

 

那么现在

nacos也注册进去了

setinel熔断也开了

路由设置也配好了

还要什么自行车,一个启动类,给他 Run 起来,就可以搞起

 

注意,访问路径是: http://路由网关IP:路由网关Port/服务名/**  (和Zuul一样的)

随便调用个接口,能够成功获取响应就是成功啦!多开几个同样的服务跑起来就会知道,负载均衡也是实现了的。

至此 Spring Cloud Gateway 结合Spring Cloud Alibaba全家桶的路由功能、负载均衡功能已经配置完毕。

阅读全文
EA PLAYER &

历史记录 [ 注意:部分数据仅限于当前浏览器 ]清空

      00:00/00:00