当前位置:   article > 正文

tcp out of order解决_分布式集群解决方案 学习笔记

tcp out if order nginx

回到目录:

OrangeZh:拉勾教育:JAVA高薪训练营 学习技术篇​zhuanlan.zhihu.com

介绍

文章内容输出来源:拉勾教育 Java高薪训练营

分布式集群解决方案相关

什么是分布式?什么是集群?

分布式:把一个系统拆分成多个子系统,每个子系统负责各自的功能,独立部署

集群啊:多个实例共同工作,将一个应用多份部署

一、一致性Hash算法

Hash算法应用:密码学的安全加密(MD5、SHA等),数据存储和查找(Hash表,查询效率高)

Hash表:Hash表的查询效率与Hash算法关系密切,Hash算法能够让数据平均分布,既能节省空间又能提高查询效率

Hash算法在分布式集群架构中的应用场景

分布式集群架构Redis、Hadoop、ElasticSearch,Mysql分库分表,Ngnix负载均衡等

主要应用场景:

  • 请求的负载均衡(ngnix的ip_hash策略)
  • IP_Hash策略:可以在客户端ip不变的情况下,将请求始终路由到同一服务器,实现会话粘滞,避免处理Session共享问题
  • 分布式存储

普通Hash算法存在的问题

服务器宕机或者新加服务器会出现重新求模问题,会导致大量客户端会话丢失

一致性Hash算法

负载均衡算法中的一种,相同的请求到固定的服务器上,当服务器宕机或者新增机器时只影响少部分请求到不同的服务器上;

原理:Hash环,根据服务器IP或者其它取hash值,置于hash环中,每个请求取hash,置于hash环中,按照顺时针方向取,遇到的第一个服务器hash就是处理此请求的服务器;

当服务器宕机或者新增服务器时只会影响宕机或者新增服务器顺时针相关的服务器,不会影响其它;

为了避免数据倾斜,加入了虚拟节点,让每个服务器计算多个虚拟hash值,让每个节点在环中分布得更加均匀

Hash算法:

  1. /**
  2. * Hash算法,性能高,碰撞率低
  3. *
  4. * @param key String
  5. * @return Long
  6. */
  7. public Long hash(String key) {
  8. ByteBuffer buf = ByteBuffer.wrap(key.getBytes());
  9. int seed = 0x1234ABCD;
  10. ByteOrder byteOrder = buf.order();
  11. buf.order(ByteOrder.LITTLE_ENDIAN);
  12. long m = 0xc6a4a7935bd1e995L;
  13. int r = 47;
  14. long h = seed ^ (buf.remaining() * m);
  15. long k;
  16. while (buf.remaining() >= 8) {
  17. k = buf.getLong();
  18. k *= m;
  19. k ^= k >>> r;
  20. k *= m;
  21. h ^= k;
  22. h *= m;
  23. }
  24. if (buf.remaining() > 0) {
  25. ByteBuffer finish = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);
  26. finish.put(buf).rewind();
  27. h ^= finish.getLong();
  28. h *= m;
  29. }
  30. h ^= h >>> r;
  31. h *= m;
  32. h ^= h >>> r;
  33. buf.order(byteOrder);
  34. return h;
  35. }

实现一致性Hash算法

  1. // 定义服务器ip
  2. private static String[] tomcatServers = new String[]{"123.111.0.0", "123.101.3.1", "111.20.35.2", "123.98.26.3"};
  3. // 定义客户端IP
  4. private static String[] clients = new String[]{"10.78.12.3", "113.25.63.1", "126.12.3.8"};
  5. /**
  6. * 一致性Hash算法 含虚拟节点
  7. */
  8. private static void consistentHashWithVirtual() {
  9. //step1 初始化:把服务器节点IP的哈希值对应到哈希环上
  10. SortedMap<Integer, String> hashServerMap = new TreeMap<>();
  11. // 定义针对每个真实服务器虚拟出来几个节点
  12. int virtualCount = 3;
  13. for (String tomcatServer : tomcatServers) {
  14. // 求出每一个ip的hash值,对应到hash环上,存储hash值与ip的对应关系
  15. int serverHash = Math.abs(tomcatServer.hashCode());
  16. // 存储hash值与ip的对应关系
  17. hashServerMap.put(serverHash, tomcatServer);
  18. // 处理虚拟节点
  19. for (int i = 0; i < virtualCount; i++) {
  20. int virtualHash = Math.abs((tomcatServer + "#" + i).hashCode());
  21. hashServerMap.put(virtualHash, "----由虚拟节点" + i + "映射过来的请求:" + tomcatServer);
  22. }
  23. }
  24. //step2 针对客户端IP求出hash值
  25. System.out.println("==========>>>> ConsistentHashWithVirtual <<<<==========");
  26. for (String client : clients) {
  27. int clientHash = Math.abs(client.hashCode());
  28. //step3 针对客户端,找到能够处理当前客户端请求的服务器(哈希环上顺时针最近)
  29. // 根据客户端ip的哈希值去找出哪一个服务器节点能够处理()
  30. SortedMap<Integer, String> integerStringSortedMap = hashServerMap.tailMap(clientHash);
  31. if (integerStringSortedMap.isEmpty()) {
  32. // 取哈希环上的顺时针第一台服务器
  33. Integer firstKey = hashServerMap.firstKey();
  34. System.out.println("客户端:" + client + " 被路由到服务器:" + hashServerMap.get(firstKey));
  35. } else {
  36. Integer firstKey = integerStringSortedMap.firstKey();
  37. System.out.println("客户端:" + client + " 被路由到服务器:" + hashServerMap.get(firstKey));
  38. }
  39. }
  40. }

算法实现:一致性Hash算法

Ngnix配置一致性Hash负载均衡策略

ngx_http_upstream_consistent_hash 模块是⼀个负载均衡器,使⽤⼀个内部⼀致性hash算法来选择合适的后端节点。

该模块可以根据配置参数采取不同的⽅式将请求均匀映射到后端机器

  • consistent_hash $remote_addr:可以根据客户端ip映射
  • consistent_hash $request_uri:根据客户端请求的uri映射
  • consistent_hash $args:根据客户端携带的参数进⾏映

二、集群时钟同步问题

问题:在集群机器中可能存在不同服务器之间服务器时间不一致

解决方法:

  • 场景1:分布式集群中各个服务器节点都可以连接互联⽹
  1. #使⽤ ntpdate ⽹络时间同步命令
  2. ntpdate -u ntp.api.bz #从⼀个时间服务器同步时间
  3. #windows有计划任务
  4. #Linux也有定时任务,crond,可以使⽤linux的定时任务,每隔10分钟执⾏⼀次ntpdate命令
  • 场景2:分布式集群中某⼀个服务器节点可以访问互联⽹或者所有节点都不能够访问互联⽹
    选取集群中的⼀个服务器节点A(172.17.0.17)作为时间服务器(整个集群时间从这台服务器同步,如果这台服务器能够访问互联⽹,可以让这台服务器和⽹络时间保持同步,如果不能就⼿动设置⼀个时间)
    ⾸先设置好A的时间
    把A配置为时间服务器(修改/etc/ntp.conf⽂件)
  1. 1、如果有 restrict default ignore,注释掉它
  2. 2、添加如下⼏⾏内容
  3. restrict 172.17.0.0 mask 255.255.255.0 nomodify notrap # 放开局
  4. 域⽹同步功能,172.17.0.0是你的局域⽹⽹段
  5. server 127.127.1.0 # local clock
  6. fudge 127.127.1.0 stratum 10
  7. 3、重启⽣效并配置ntpd服务开机⾃启动
  8. service ntpd restart
  9. chkconfig ntpd on

集群中其他节点就可以从A服务器同步时间了

ntpdate 172.17.0.17

三、分布式ID解决方案

因为分库分布后不能使用自动递增主键,所以数据库分库分表时需要一种能够产生全局唯一ID的方案

使用UUID

UUID 是指Universally Unique Identifier,翻译为中⽂是通⽤唯⼀识别码

产⽣重复 UUID 并造成错误的情况⾮常低,是故⼤可不必考虑此问题。

Java中得到⼀个UUID,可以使⽤java.util包提供的⽅法:

java.util.UUID.randomUUID()

独⽴数据库的⾃增ID(性能低,不可靠,部署成集群比较麻烦)

单独维护一个ID自增表,但性能低,如果库挂了就无法获取了,可靠性不够高

SnowFlake 雪花算法(可以⽤,推荐)

雪花算法是Twitter推出的⼀个⽤于⽣成分布式ID的策略。

雪花算法是⼀个算法,基于这个算法可以⽣成ID,⽣成的ID是⼀个long型,那么在Java中⼀个long型是8个字节,算下来是64bit,如下是使⽤雪花算法⽣成的⼀个ID的⼆进制形式示意:

29d924061d59700fc2f1a86601d4502c.png


SnowFlake雪花算法

一些互联⽹公司也基于上述的⽅案封装了⼀些分布式ID⽣成器,⽐如滴滴的tinyid(基于数据库实现)、百度的uidgenerator(基于SnowFlake)和美团的leaf(基于数据库和SnowFlake)等

算法实现:SnowFlake 雪花算法

使用Redis的Incr命令获取全局唯⼀ID(推荐)

存一个全局的自增数到一个redisKey中

四、分布式调度问题(定时任务的分布式执行)

定时任务场景

定时任务形式:每隔⼀定时间/特定某⼀时刻执⾏

例如:

  1. 订单审核、出库
  2. 订单超时⾃动取消、⽀付退款
  3. 礼券同步、⽣成、发放作业
  4. 物流信息推送、抓取作业、退换货处理作业
  5. 数据积压监控、⽇志监控、服务可⽤性探测作业
  6. 定时备份数据
  7. ⾦融系统每天的定时结算
  8. 数据归档、清理作业
  9. 报表、离线数据分析作业

什么是分布式调度

1)运⾏在分布式集群环境下的调度任务(同⼀个定时任务程序部署多份,只应该有⼀个定时任务在执⾏)

2)分布式调度—>定时任务的分布式—>定时任务的拆分(即为把⼀个⼤的作业任务拆分为多个⼩的作业任务,同时执⾏)

9912a91597eb9fe3d6ef3599e6ef915a.png


分布式任务调度

定时任务与消息队列的区别

共同点:

  • 异步处理 ⽐如注册、下单事件
  • 应⽤解耦 不管定时任务作业还是MQ都可以作为两个应⽤之间的⻮轮实现应⽤解耦,这个⻮轮可以中转数据,当然单体服务不需要考虑这些,服务拆分的时候往往都会考虑
  • 流量削峰 双⼗⼀的时候,任务作业和MQ都可以⽤来扛流量,后端系统根据服务能⼒定时处理订单或者从MQ抓取订单抓取到⼀个订单到来事件的话触发处理,对于前端⽤户来说看到的结果是已经下单成功了,下单是不受任何影响的

本质不同:

  • 定时任务作业是时间驱动,⽽MQ是事件驱动
  • 时间驱动是不可代替的,⽐如⾦融系统每⽇的利息结算,不是说利息来⼀条(利息到来事件)就算⼀下,⽽往往是通过定时任务批量计算;
  • 定时任务作业更倾向于批处理,MQ倾向于逐条处理;

定时任务的实现方式(任务调度框架Quartz)

定时任务的实现⽅式有多种。早期没有定时任务框架的时候,我们会使⽤JDK中的Timer机制和多线程机制(Runnable+线程休眠)来实现定时或者间隔⼀段时间执⾏某⼀段程序;后来有了定时任务框架,⽐如⼤名鼎鼎的Quartz任务调度框架,使⽤时间表达式(包括:秒、分、时、⽇、周、年)配置某⼀个任务什么时间去执⾏

  1. <!--任务调度框架quartz-->
  2. <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
  3. <dependency>
  4. <groupId>org.quartz-scheduler</groupId>
  5. <artifactId>quartz</artifactId>
  6. <version>2.3.2</version>
  7. </dependency>

示例实现:示例

分布式调度框架Elastic-Job

Elastic-Job是当当⽹开源的⼀个分布式调度解决⽅案,基于Quartz⼆次开发的,由两个相互独⽴的⼦项⽬Elastic-Job-Lite和Elastic-Job-Cloud组成

Elastic-Job的github地址:https://github.com/elasticjob

五、Session共享(一致性)问题

Session问题原因分析

路由后到了不同的服务器,导致了Session丢失,会出现重复登录问题

936a02f347ec4d088eaf6819fc55912c.png


Session问题

解决Session⼀致性的⽅案

  • Nginx的 IP_Hash 策略(可以使⽤)
    同⼀个客户端IP的请求都会被路由到同⼀个⽬标服务器,也叫做会话粘滞
    优点:配置简单,不⼊侵应⽤,不需要额外修改代码
    缺点:服务器重启Session丢失、存在单点负载⾼的⻛险、单点故障问题
  • Session复制(不推荐)
    多个tomcat之间通过修改配置⽂件,达到Session之间的复制,通过TCP进行同步
    优点:不⼊侵应⽤、便于服务器⽔平扩展、能适应各种负载均衡策略、服务器重启或者宕机不会造成Session丢失
    缺点:性能低、内存消耗、不能存储太多数据,否则数据越多越影响性能、延迟性
  • Session共享,Session集中存储(推荐)
    Session的本质就是缓存,那Session数据可以存入缓存中间件Redis中
    优点:能适应各种负载均衡策略、服务器重启或者宕机不会造成Session丢失、扩展能⼒强、适合⼤集群数量使⽤
    缺点:对应⽤有⼊侵,引⼊了和Redis的交互代码
    Spring Session使得基于Redis的Session共享应⽤起来⾮常之简单

回到目录:

OrangeZh:拉勾教育:JAVA高薪训练营 学习技术篇​zhuanlan.zhihu.com
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/270208
推荐阅读
相关标签
  

闽ICP备14008679号