ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

熟练掌握SpringCloud流行技术栈,如Nacos、Seata、Zookeeper、Dubbo、OpenFeign、GateWay、Sentinel、SkyWalking和Discovery。熟悉

熟练掌握SpringCloud流行技术栈,如Nacos、Seata、Zookeeper、Dubbo、OpenFeign、GateWay、Sentinel、SkyWalking和Discovery。熟悉 Nacos注册中心原理nacos1.x1.提供方调用rest接口发起服务注册注册自己的ip:端口2.消费方每隔10秒拉取服务提供方注册列表3.消费方提供方都需要和nacos每隔5秒进行心跳检测15秒没有心跳标志为不健康30秒没有心跳标志位下线。4.无论是消费方的上线或者是提供方的下线都会进行udp快速广播通知全部的服务实例5.nacos集群之间的数据同步采用Distro协议。nacos2.x把所有的单向的http升级为grpc协议心跳包仍旧需要发送但是得益于http2.0的信道复用模式不需要在进行频繁的TCP握手挥手了而且也不需要再额外发送 UDP 包广播了大大提升了性能配置中心原理nacos客户端一启动会根据bootstrap.yaml上配置的nacos地址去nacos config上拉取自己服务的配置文件信息。之后进行长轮询获取到变化以后采用事件发布机制通知本地属性的变化。短连接请求/响应立即结束释放资源。长连接建立连接以后保持连接传输数据。长轮询请求服务器没数据则挂起一段时间会超时有数据则返回。HTTP1.0短连接除非显示指定Keep-Alive。HTTP2.0长连接多路复用。gRPCHTTP2.0Protobuf压缩。配置优先级 默认nacos />DubboDubboServiceProviderDubboReferenceConsumer我从数据从消费方发送到提供方讲解先获取到对方的ipport接着是代理层默认使用jdk的动态代理创建代理对象代理真正的数据传输操作。其次是序列化层发送方将对象使用默认的protobuf序列化。接着是网络协议层封装对应的Dubbo协议可选grpc。接着是传输策略层可选负载均衡的策略默认是随机策略。接着是网络传输层通过持续运行的socket服务容器用了netty框架的主从Reactor多线程NIO模型的代码搭配上操作系统的epoll事件驱动机制读取数据流的数据并且解析协议反序列化对象。还会有Monitor监控节点的调用次数和响应时间提供健康运维。Dubbo SPI机制可以自定义负载均衡、网络协议和序列化方式。META-INF/dubbo com.example.Robot optimusPrimecom.example.OptimusPrimeJDK SPIsrc/main/resources/META-INF/services com.example.spi.GreetingService此接口的具体实现com.example.spi.impl.EnglishGreetingServicecom.example.spi.impl.ChineseGreetingServiceServiceLoaderGreetingServiceloaderServiceLoader.load(GreetingService.class);// 遍历并调用服务提供者的方法for(GreetingServiceservice:loader){service.sayGreeting();}Spring boot 2.x resources/META-INF spring.factorysSpring boot 3.xresources/META-INF org.springframework.boot.autoconfigure.AutoConfiguration.importsOpenFeignFeignClient(valueuser-service, fallbackFactoryUserServiceFallbackFactory.class)public interface UserService{GetMapping(/users/{id})User getUserById(PathVariable(id)Longid);//... 其他方法}Component public class UserServiceFallbackFactory implements FallbackFactoryUserService{Override public UserService create(Throwable cause){returnnewUserService(){Override public User getUserById(Longid){// 降级逻辑返回一个默认值或错误信息returnnew User(0L,Default User,Default Address);}//... 其他方法也实现降级逻辑};}}OpenFeign 内置了Ribbon作为客户端负载均衡器可以自动对服务进行负载均衡默认带区域的轮询。Spring cloud GateWayspring: cloud: gateway: routes: - id: service1_route uri: http://localhost:8081# 目标URIorder:0# 路由优先级数字越小优先级越高predicates: -Path/service1/**# 断言匹配以 /service1/ 开头的请求路径filters: -AddRequestHeaderX-Request-Id,123456# 过滤器为请求添加一个头信息- id: service2_route uri: http://localhost:8082 order:1predicates: -Path/service2/** filters:# 可以配置多个过滤器以列表形式给出-AddRequestHeaderAnother-Header, abcdef -RewritePath/service2/(?segment.*), /$\{segment}# 过滤器重写请求路径自定义过滤器。Component public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactoryCustomGatewayFilterFactory.Config{publicCustomGatewayFilterFactory(){super(Config.class);}Override public ListStringshortcutFieldOrder(){// 定义配置参数的顺序如果有的话returnCollections.singletonList(myParam);}Override public GatewayFilter apply(Config config){return(exchange, chain)-{// 自定义过滤逻辑 //... // 继续处理请求链returnchain.filter(exchange);};}public static class Config{// 可以在这里定义配置参数 private String myParam;//... getters, setters}}Sentinel四种限流算法1.固定窗口算法10秒一个窗口每个10s超过10请求就拒绝会存在临界的第9秒9次后一个窗口的第一秒9次这就相当于两秒钟18次已经没有触发限流。2.滑动窗口算法类似Sentinel的算法就是将固定的窗口划分的十分小只有200ms。5 qps ! ! ! ! !第一个 5 个 200ms 窗口 最多5个请求若超出了则这1s内全部的请求会被拦截向后滑动200ms一个小窗口在计算新的1s窗口的请求量进行限流判断。3.漏桶算法类似队列消息发生填充是随机速度的但是消费方的消费速度总是恒定的。3.令牌漏桶算法类似漏桶算法消息发生填充是随机速度的但是消费方的最大的消费速度总是恒定的但是相比较漏桶算法最大的不同在于桶里面已经预置了令牌对于短时间大流量也能匹配上一定的消费速度去处理而漏桶算法永远都是恒定的。页面限流截图防范DDos。低级的DDos就是相同ip、客户端ua、相同账号这样发起大规模请求调用这样是小儿科Nginx就有针对ip请求速度的限制网关也能进行限制。高级的DDos调用肉鸡发起最真实的恶意请求这样根本上防护不了只能说保护服务不宕机、卡死。3种攻击方式带宽攻击比如文件上传接口直接把服务的带宽占满或者频繁进行静态资源的访问这样可以借助厂商的cdn或者是使用公共的对象存储把图片放oss让静态压力由云服务厂商的大带宽去处理。协议攻击、如TCP三次握手直接就发送第一个syn之后不连接了tcp协议在服务端就会进入半连接状态在超时时间之内打满。这个可以让运维优化TCP参数调整半连接的队列。后端接口攻击直接攻击对外暴露的接口这种只能通过高防服务器云厂商大带宽服务器和高性能处理加上它们的ai识别算法去做前置拦截过滤掉恶意流量。使用Sentinel进行ToC接口的限流防止瞬时流量过大导致cpu内存io资源占满任务队列无限排队引起服务或者数据库宕机、卡死因为某一个接口导致全服务不可用防范DDos。落地实践我们会在GatewaySentinel整合网关所有的toc接口能都暴露出来针对文件上传之类的占带宽的接口会有整体的限制。这个限制多少预估算加上测试去测比如最大上传5M图片假设我们网络带宽最大是1000Mbps那就是1000/8/5这个是上限取50%就是限制的qps当然准确都需要测试去测防止由于带宽把重要的动态数据请求阻塞。针对动态数据请求的攻击首先让测试测出每个服务单压每个接口最大的承受能力是多少比如针对某一服务发起超高的攻击qps看服务能处理的qps是多少两个qps不一样差值其实都是在tomcat的等待队列等待那这个服务响应的qps就是需要接口限制的qps了因为已经开始等待了并且数据库和cpu内存都是这一个接口在使用与其累计的等待不如直接拒绝。拒绝策略都是直接响应失败并且在网关层会往mq发送消息记录用户的操作入参和userId如真的出现ddos对于正常的用户下单我们也能在后续发短信或者补偿优惠的方式进行二次挽救。其次出现高流量在k8s环境额外预留一些Iass资源使用hpa动态扩容的方式来应对突发流量也是有效的但是业务接口一般瓶颈在于数据库。整体的目标就是不能让ddos瞬时流量击垮冷系统。客户端引入sentinel的核心依赖配置上sentinel 服务端的地址java -jar 启动sentinel 服务web控制台可以进入配置并且把流控配置写入nacos。SkyWalking全链路监控配合web页面能够显示出每个接口在各个服务包括mqmysqlredis的调用链路以及各执行时间。针对某个业务方法每个方法的执行耗时已经入参、出参。SkyWalking我们是这样用的用的是SkyWalking on Kubernetes它是一个自定义CRD operator的方式用过yaml增加annotation的方式去动态的为java的pod增加skywalking的探针不用自己去java -jar 指定agent了。对于重要的服务订单、支付、库存核心服务会开启debug模式的日志级别并且打印核心方法每个方法调用栈的入参和出参并且每一步都打上完整详细的日志通过skywalking的gpc上传日志到es。然后专门的EFK日志系统也会收集一份日志。当有用户有线上问题需排查的时候记录下他大致操作的时间做了什么操作输入了什么参数得到了什么响应。之后我们去es上根据时间索引查询大致的时间范围以及对应的userid操作的接口确定下skywalking的tranreidskywalking上很直观的就能看到调用的链路已经那个方法那条sql出了什么问题。EFK定位大致的接口调用skywalking直观的去找问题点。第二点就是SkyWalking的报警机制监控各个服务的响应时间比如一分钟之内平均响应时间大于原来95原则的两倍。Discovery全链路灰度发布。有这样的需要就是局部的服务进行版本升级。比如下订单 首先调用 风控服务。之后风控服务的风控规则升级了增加了很多的限制规则。之后经过软件生命周期uat测试内部全测完了要上线了会单独先部署一个deploy只有一个升级版本镜像的pod。这个pod的app标签都是同一个service的也就是说feign调用会有一部分流量调到这个pod但是我们会结合Discovery在Gateway的nacos配置文件进行header头的配置也就是说只有具特定head头标识的流量才能分配到新版本的podDiscovery的作用就是流量染色灰度发布的作用。上线生产之后内部的测试员工用他们的账号拥有特别head头的账号进行测试确定风控服务没问题之后去除原有head头的限制他会热加载之后删除原有老版本的deploy扩容新版的deploy副本实例这样就做到无缝链路灰度了要是有问题那就不升级原有的用户也不受影响。也适用于是否让用户选择是否参与内部全新版本之后给她一个新版本的head头。Discovery还有一种模式是百分比分配流量的方式百分之10分配到新的版本90分配到旧的版本但是这样对于用户来讲还是会有风险。类似istio。分布式全局ID1.雪花算法8字节8*864位第一个不用后面时间戳41位工作机器ID10位序列号12位全局唯一性由于时间戳、工作机器ID和序列号三者的组合使得雪花算法能够在分布式系统中生成全局唯一的ID。时间有序性由于时间戳的存在生成的ID大致按照时间递增排序这对于需要按时间排序的场景非常有用。实践方式一次获取两批量的id一批id使用完之后异步的去请求第二批id这样就避免了id单条获取已经批量用完之后的数据拉取卡顿两个id池交互获取。我们的消息队列的自定义id就是这样的获取方式。2.redis自增我们就是用这种方式专门定义了一个id服务用于获取各类id。并且我们的业务id都会有相互关联关系。比如现在新增了一件商品需要新增分类、商品的spu、商品的sku这三个id。分类号这个是redis 从10000自增的每次加1 假设取到某个值11111。 11111那么该商品的spu从10000000开始也每次自增加1后四位和该分类的后四位一样1111。100000001111 1七个0 后面四个1这样spu可以自增到百万级别。该商品的sku1000000000后四位也是1111。这样sku可以自增到千万级别。并且当进行取模16运算分库分表的时候由于基因法的存在该商品的查询都能落到同一个库表里面。并且业务上也能直观的看出来都属于统一商品。注意前端js对于大于17位的long会丢失精度需要转成字符串返回。分布式事务对于后台业务面向运营人员的接口直接GlableTransition,一个注解开启全局的分布式锁注意在锁的时候必须确保表设计了索引尽量能直接锁到行锁级别若出现范围更新出现间隙锁最好也小批量操作因为面向运营的接口并发几乎忽略可以使用二阶段锁的seata AT模式。重点是对于toc的高并发接口最开始分清两种异常需要解决的是有程序健壮性带来的异常问题能处理的runtime Exception最常见的入参没校验带来空指针数组越界了或者是insert sql的时候字段超长了这种问题引起的分布式事务出现异常就很低级错误了。第二种就是需要解决的异常引起的分布式事务问题最常见的就是东西流量接口调用超时小概率是k8s网络引起的运维的网络隔离策略写错其次概率是程序代码没写好某些入参的情况下需要处理很久最大概率还是数据库被某些代码弄得卡死了没索引直接全表扫描了导致的超时。连接超时可以确定对方服务没做。但是服务读取超时最头疼的在于你都不知道对方服务到底成功做了还是没成功回滚了。解决方法首先从业务层面避免多层级的链式调用其次使用中央编排模式。就像A-B-C转成 A-B 响应B的结果给A以后 A-C。将长事务有个中心编排器。其次不要把微服务的数据库拆的很细比如是否对于小体量公司业务是否可以考虑服务层代码是分布式的但是数据库是同一个这样就可以控制成本地事务或者就相关联程度高的服务如订单和支付不进行独占式数据库控制成本地事务就不会有这么多问题。若是大体量Toc互联网业务处理长事务的方式是通过seata 的 saga模式写事务状态机自定义的反向补偿加上mq加上幂等判断去处理的加上幂等就能很好解决对方服务到底成功了做成功没没成功就不补偿了成功了才去反向补偿。比如我负责的核心交易层下单接口。我们是异步编排的方式读操作异步并行写操作同步执行。全局不开启任何事务只开启本地事务。第一步生成一个全局订单号调用风控服务获取商品最新的信息三件事件都是读操作可以异步并行其中某一步出异常了直接响应出错结果不涉及事务。年月日时分秒01订单来源八位会员号后四位总共20为这一步从redis查询同步阻塞至第二步调用库存服务库存服务也开启本地事务根据sku查询此商品的销售库存是否可用update stock set online_num online_num -1,lock_numlock_num1 where sku100 and online_num-1 -1;若成功则写入锁定表一条记录订单号以及锁定的商品号以及锁定数量。同步阻塞至第三步调用优惠服务一样去锁定优惠券不让用户的一张优惠券在两个订单同时存在写入优惠券的锁定表订单号优惠券号。第四步计算销售价格调用物流服务获得物流价格。第五步计算销售价格-物流价格得到支付价格和前端传过来的价格对比。若成功则生成父订单这个时候再去把这个订单id以及其他关联的用户、商品、库存和其他信息插入订单表。假设最后这一步insert出错了通过saga 的补偿策略依次进行逆向逻辑。1.订单新增取得订单号往mq发送订单逻辑删除。2.锁定优惠券取得订单号往mq发送优惠券和订单的关联锁定表逻辑删除当然需要先查询是否存在记录幂等的去做。3.库存服务也是如此。若是优惠券锁定失败就从优惠券和库存开始反向补偿。没开启任何事务也没全局锁不然对于某个商品id的库存id进行全局锁直接会导致其他线程对此商品无法下单锁库存。这里还有两个点为什么还需要开启本地事务因为需要二次保障防止mq挂了反向补偿的消息都发不了这是就会有大量的数据是不一致的。还有就是为什么一定要用消息队列补偿直接rpc调用不行因为假设我最后一步插入订单失败了这时候再用rpc调用其他服务的反向补偿接口是不合理的因为自己的服务都出现未知异常了进行远程rpc长事务补偿和正向流程执行复杂程度差不多同步rpc调用大概率不可靠了还会出错而直接发消息安全的多并且消息可以有状态存储不像rpc失败消息直接就没了。分布式锁核心公共服务争夺一个共享资源没争取到的进行队列等待。RedissonRlock的共享资源就是。在同一台redis去设置相同的key的一个hash 键值对大key就是Rlock的字符串hashkey就是java线程号的hash值v是1重入次数。当其他线程尝试占有锁是发现已经被别的线程占用了就进行订阅通过redis stream pub sub的方式再将自己进行park阻塞当次key不存在时会唤醒全部的订阅者强锁抢到锁的进行unpark。当然这是lua脚本的方式进行原子判断的。zk上面的。reenlockAQs队列加 cas强锁加park阻塞。syncroizy采用操作系统层面的monitor的源语进行锁进入锁释放内部维护等待和阻塞队列count 表示重入次数。分布式会话无状态jwt、Header签名算法。Payload用户附加数据。Signature对头部和有效载荷进行签名。优点无状态不用存跨平台通用标准、多语言支持安全性内容不可修改缺点无法撤销提前过期敏感信息泄露载荷会被解密过期问题过期重新获取无法主动续期redis的token上面的缺点都无。userIdiphash取模至同一台服务器。分布式任务分片任务。
返回列表