范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

负载均衡原理及实现

  什么是负载均衡 ?
  负载均衡( LoadBalance ),顾名思义就是把任务压力进行平衡的分摊到集群中各个操作单元(或机器)上,使得避免集群中部分机器压力过大而部分机器过于空闲。经过负载均衡,使得每个机器获取适合自己的处理能力负载。
  负载均衡的分类
  负载均衡可分为硬件负载均衡和软件负载均衡。
  硬件负载均衡比如F5、NetScaler等价格比较昂贵,一般开发中很难接触到。但软件负载均衡还是很容易接触到的,比如大家常见的Nginx、LVS等。
  软件负载均衡又可以分为四层负载均衡和七层负载均衡。
  所谓四层负载均衡就是在网络层基于IP+端口的负载均衡。也就是通过报文中的目标地址和端口,在加上负载均衡设备的选择方式从而选择最终对应的服务器。
  所谓七层负载均衡就是在四层的基础上在应用层根据用户的http请求头和URL等信息将请求转发到特定的服务器上。LVS(Linux Virtual Server)为四层负载均衡,Nginx可以做四层也可以做七层。
  负载均衡的常见算法
  对于软件中实现的负载均衡,常见的负载均衡算法有轮询(Round Robin)法、随机(Random)法、加权轮询(Weight Round Robin)法、加权随机(Weight Random)法、源地址哈希(Hash)法、一致性哈希(Consistent Hash)法等。
  1、轮询(Round Robin)法
  将接收到的请求按照顺序依次分配给各个服务器对外提供服务。
  代码如下:
  #include   int position = 0;  int totalServer = 4; pthread_mutex_t mut;//互斥锁  char * serverInfo[]= { "192.168.1.1", "192.168.1.2", "192.168.1.3", "192.168.1.4",  };   char * getServerUrl() {   char * ret = NULL;   pthread_mutex_lock(&mut);  if(position >= totalServer ) position = 0;  position++;  ret = serverInfo[position];  pthread_mutex_unlock(&mut);  return ret; }
  轮询就是对各个服务节点依次顺序遍历,当请求的次数 position 达到最大服务节点数量时,重新从0开始。
  其实可以把服务节点列表想象成一个圆中的各个圆点,当访问请求次数到达最大值时也就循环是一周,然后在从0开始。
  优点 :对于各个服务节点对外提供服务的次数绝对均衡。
  缺点 :由于使用了 position 获取服务节点,因此对 position 的修改要做到互斥,所以对 性能会造成一定的影响,同时对于低承载能力的服务节点不友好,因为它要和高承载能力的服务节点接受同样的压力。
  2、随机(Random)法
  根据后端服务节点的个数,从中随机选择其中一个节点对外提供服务。
  代码如下:  char * getServerUrl() { int i = rand() % totalServer;  return serverInfo[i];  }
  优点: 算法实现简单,当请求的次数增大时,各个服务节点处理的请求数量近似于均衡。
  缺点: 和轮训算法一样,对于低承载能力的服务节点不友好。
  3、加权轮询(Weight Round Robin)法
  上述的轮询法和随机法存在一个共同的缺点,就是各个服务器节点负载相对均衡,但是当各个服务器节点载能力不同时,会对低承载能力的服务器节点压力过大。
  正是由于这个缺点,后续有了加权轮询法、加权随机法,该算法的实现就是给不同配置的服务器节点,配置不同的权重值,后续分配请求时,对权重高的服务器节点,多分配点,对权重低的服务器节点,少分配点。
  如下实现了一个加权轮询算法:
  #include  #include  #include   #define IP_LEN  22  typedef struct {     int weight;     int cur_weight;//哪个server当权权重最大,就分配个谁     char ip[IP_LEN]; }serverInfo;  int getWeightSum(int *weight, int size) {     int i = 0;     int total = 0;      for(i = 0; i < size; i++)     {         total += weight[i];     }      return total; }   serverInfo * initServer(char **ips, int *weight, int size) {     int i = 0;     serverInfo * servers = calloc(size+1, sizeof(serverInfo));      for(i = 0; i < size; i++)     {         servers[i].weight = weight[i];         memcpy(servers[i].ip, ips[i], IP_LEN);         servers[i].cur_weight = 0;     }      return servers; }  /**该算法参看nginx中平滑的加权轮询算法,该算法会保证生成的序列比较均匀   而普通的加权轮询算法在某种权重下会出现不均匀的序列 */ int getServerUrlIndex(serverInfo *s, int size) {     int i=0;     int index = 0; //记录当前权重最大的server索引     int total = 0; //记录权重总和      for(i = 0; i < size; i++)     {         s[i].cur_weight += s[i].weight;         total += s[i].weight;         //记录权重最大的索引节点         if(s[index].cur_weight < s[i].cur_weight)         {             index = i;         }     }     //把当前权重最大的减去权重总和,这时候当前权重会减小,所以下次分配就不一定还是该server了     s[index].cur_weight -=total;     //返回当前权重最大的server索引     return index;    }  void getServerUrl(serverInfo *s, int *weight, int size) {     int i = 0;     int index = 0;     int total = getWeightSum(weight, size);      for(i=0; i %d ", s[index].ip, s[index].weight);     }  }   int main() {     int i = 0;     int weight[] = {5,2,3};     char *ips[] = {"192.168.1.1", "192.168.2.2", "192.168.3.3"};     int size = sizeof(weight)/sizeof(int);     serverInfo *s = initServer(ips, weight, size);      for(i = 0; i < size; i++)     {         printf("server init is %s => %d ", s[i].ip, s[i].weight);      }       printf("getServerUrl: ");     getServerUrl(s, weight, size);      return 0;  }   //得到结果如下: server init is 192.168.1.1 => 5 server init is 192.168.2.2 => 2 server init is 192.168.3.3 => 3 getServerUrl: get server is 192.168.1.1 => 5 get server is 192.168.3.3 => 3 get server is 192.168.2.2 => 2 get server is 192.168.1.1 => 5 get server is 192.168.1.1 => 5 get server is 192.168.3.3 => 3 get server is 192.168.1.1 => 5 get server is 192.168.2.2 => 2 get server is 192.168.3.3 => 3 get server is 192.168.1.1 => 5
  优点 :加权轮询能够使用权重解决不同承载能力的服务器节点。
  缺点  : 需要使用一定的机制解决某些情况下生成的序列是不均匀的问题。
  4、源地址哈希(Hash)法
  根据客户端的 ip 经过 hash 获取一个 hash 值,然后根据该 hash 值和服务器节点总数进行取模运算,取得对应的服务器节点 。 char * getServerUrl() { int i = hashcode(clientIP) % totalServer; return serverInfo[i];  }
  优点  : 当服务器列表不变时,只要请求源地址不变,总会请求到相同的目标服务器,这样就可以在客户端-服务器之间构建一个有状态 session。
  缺点  : 当有大量的获取客户端时,会造成部分服务器过于繁忙,导致负载不均衡。另外,当某个服务器节点挂掉后,后续 hash 到该节点的所有请求都会得不到响应。
  5、一致性哈希(Consistent Hash)法
  传统的hash算法一般就是 key 和节点总数进行 mod 运算 mod(key, n),但这样会带来一个问题,就是当扩容或者缩容时,他们的映射关系就变成了mod (key, n+1) 或 mod (key, n-1),这会导致所有的 key 都会出现新的映射关系,因此很多 key 就会出现映射失败。
  为了解决普通 hash 这种扩展能力和容错能力差的问题,就出现了一致性 hash(Consistent Hashing)的概念,采用一致性 hash 时,当出现扩容或者缩容时,影响的 key 只有少数部分,对全局影响不大。
  一致性 hash 采用对每个服务节点进行计算 hash值,该hash值的范围在(0~2³²-1)中,我们把这些数字收尾相连构成一个范围在(0~2³²-1)的 hash 环中,然后把各个服务器节点经过 hash 运算得到对应的值,然后散列到 hash 环中,如下图,
  当请求到来时,根据请求的唯一标志,比如请求的客户 IP 等经过 hash 运算得到一个 hash 值,然后根据该值从 hash 环上以顺时针的方向查找,得到一个离自己最近的服务器节点,该服务器节点也就是该请求对应的服务器。如下图,客户端请求经过 hash,从 hash 环中顺时针找到 node2 服务节点。
  经过上述介绍我们可以发现一个问题,也就是当服务节点较少时,会出现数据倾斜问题,也就失去了平衡性,如下图,我们会发现很多都分配给了node2节点了。
  为了防止这种数据倾斜,我们采用虚拟节点来保证平衡性。虚拟机节点其实就是服务器节点的复制,我们把一个服务器节点映射多个虚拟节点,当请求经过 hash 运算后从hash 环上以顺时针的方向查找到虚拟机节点时,我们就把虚拟节点对应的实际服务节点作为服务器。虚拟节点如下图
  比如客户端请求到来,根据客户端的 key1 经过 hash 计算映射到 hash 环中如下图,从该点顺时针找到了服务器节点 3 的虚拟节点,因此把服务器节点 3 作为请求对应的服务器。
  当某个服务节点宕机(比如服务节点2),那么请求对象最终落到了服务节点 3,对于整个系统的影响也就是 key1 和 node2 之间的范围。如下图,
  新增服务节点同理,当 key1 和 node3 之间新增了一个服务节点2,那么 key1 经过 hash 查找服务节点时会找到服务节点 2。
  一致性hash的实现
  本次分析一下开源项目 ketama 中的一致性 hash 算法的实现 。
  在 ketama 中为了平衡性也采用了虚拟机节点,同时为了不同的服务节点的承载 能力也使用了权重,因此权重高的,相应的虚拟机节点也就相应的多,服务器节点也就处理的请求多。
  ketama 中使用如下结构记录服务节点信息 。 typedef struct { unsigned int point; //圆环上的点 char ip[22]; //实际服务节点ip } mcs;  // 服务器信息,主要记录服务器的ip地址和权重值 typedef struct { char addr[22]; //服务器ip地址 unsigned long memory; // 权重值 } serverinfo;  // 以下数据结构就是continuum环上的结点,环上的每个点其实代表了一个ip地址,该结构把点和ip地址一一对应起来。 typedef struct { int numpoints; //在环上的点的总数同时也是 array数组元素的总个数 void* modtime; // 更改时间 void* array; // mcs数组 该数组时以mcs中point从小到大排过序的,为了方便从环上找响应的服务节点 } continuum;
  一致性hash环的创建
  /* 一致性hash环的创建 该函数是创建continuum的核心函数,它先从配置文件中读取集群服务器ip和端口,以及权重信息。 创建continuum环,并把这些服务器信息和环上的数组下标对应起来。 */ static int ketama_create_continuum( key_t key, char* filename ) {       ...     unsigned int numservers = 0; // 该变量来记录共从配置文件中共读取了多少个服务器     unsigned long memory; // 该变量是配置文件中所有服务器权重值的总和     serverinfo* slist; // 从配置文件中读取到的服务器信息,包括ip地址,端口,权重值      // 从配置文件filename中读取服务器信息,把服务器总数保存到变量numservers中,把所有服务器的权重值保存到memory中     slist = read_server_definitions( filename, &numservers, &memory );      /*     以下代码开始构建continuum环     平均每台服务器要在这个环上布160个点,这个数组的元素个数就是服务器个数*160。     具体多少个点,需要根据事情的服务器权重值进行计算得到。     为什么要选择160个点呢?主要是通过md5计算出来的是16个整数,把这个整数分成4等分,每份是4位整数。     而每进行一次hash计算,我们可以获得4个点。    */     mcs continuum[ numservers * 160 ];     unsigned int i, k, cont = 0;      for( i = 0; i < numservers; i++ ) // 遍历所有服务器开始在环上部点     {         float pct = (float)slist[i].memory / (float)memory; // 计算服务器i在所有服务器权重的占比         /* 由于计算一次可以得到4个点,所有对每一台机器来说,总的计算只需要计算40*numservers次。            按权重占比进行划分,就是以下的计算得到的次数          */         unsigned int ks = floorf( pct * 40.0 * (float)numservers );          //ks是每个ip地址对应的总点数         for( k = 0; k < ks; k++ ) // 计算出总次数,每次可以得到4个点         {             /* 40 hashes, 4 numbers per hash = 160 points per server */             char ss[30];             unsigned char digest[16];             // 通过计算hash值来得到下标值,该hash值是字符串:"-n",其中的n是通过权重计算出来的该主机应该部点的总数/4。             sprintf( ss, "%s-%d", slist[i].addr, k );             ketama_md5_digest( ss, digest ); // 计算其字符串的md5值,该值计算出来后是一个unsigned char [16]的数组,也就是可以保存16个字节               int h;             for( h = 0; h < 4; h++ )// 共有16个字节,可以处理4次,得到4个点的值             {                 /*                   把计算出来的连续4位的数字,进行移位。                   把第一个数字移到一个整数的最高8位,后面的一次移动次高8位,后面一次补零,这样就得到了一个32位的整数值。                */                 continuum[cont].point = ( digest[3+h*4] << 24 )                                       | ( digest[2+h*4] << 16 )                                       | ( digest[1+h*4] <<  8 )                                       |   digest[h*4];                  // 复制对应的ip地址到该点上                 memcpy( continuum[cont].ip, slist[i].addr, 22 );                 cont++;             }         }     }     free( slist );      /*Sorts in ascending order of "point"        以下代码对计算出来的环上点的值进行排序,方便进行查找       这里要注意:排序是按照point的值(计算出来的整数值)进行的,也就是说原来的数组下标顺序被打乱了。      */     /* Sorts in ascending order of "point" */     qsort( (void*) &continuum, cont, sizeof( mcs ), (compfn)ketama_compare );     /*到这里算法的实现就结束了,环上的点(0^32整数范围内)都已经建立起来,每个点都是0到2^32的一个整数和ip地址的结构。     这样查找的时候,只是需要hash(key),并在环上找到对应的数的位置,取得该节点的ip地址即可。     */      ...      return 1; }
  hash环的构建也很简单:
  1、从配置文件中读取服务器信息列表,获取总的权重值。
  2、创建numservers * 160 个point点也就是虚拟机节点,然后根据权重进行hash运算分配这些point点,权重高的分配的越多。同时对这些point点进行从小到大排序,为何后续查找方便。
  在环上查找元素
  //计算key的hash值的实现方法 unsigned int ketama_hashi( char* inString )  { unsigned char digest[16]; // 对key的值做md5计算,得到一个有16个元素的unsigned char数组 ketama_md5_digest( inString, digest );   //取数组中的前4个字符,并移位,得到一个unsigned int的hash值 return (unsigned int)(( digest[3] << 24 ) | ( digest[2] << 16 ) | ( digest[1] << 8 ) | digest[0] ); }   //在环上查找相应的结点 mcs* ketama_get_server( char* key, ketama_continuum cont ) { unsigned int h = ketama_hashi( key ); // 计算key的hash值,并保存到变量h中 int highp = cont->numpoints; // 该变量cont->numpoints是总的数组埋点数 mcs (*mcsarr)[cont->numpoints] = cont->array; // 数组结点的值 int lowp = 0, midp; unsigned int midval, midval1;  // pide and conquer array search to find server with next biggest // point after what this key hashes to while ( 1 ) { // 从数组的中间位置开始找 // 注意此时的数组是按照point的值排好序了 midp = (int)( ( lowp+highp ) / 2 ); // 若中间位置等于最大点数,直接绕回到0位置 if ( midp == cont->numpoints ) return &( (*mcsarr)[0] ); // if at the end, roll back to zeroth // 取的中间位置的point值 midval = (*mcsarr)[midp].point; /* 获取 midp 上限和下限, 若midp 为0 则midval1 为0 ,否则取midp-1的point,也即是 获取 h 在如下范围内 (*mcsarr)[midp-1].point < h < (*mcsarr)[midp] */ midval1 = midp == 0 ? 0 : (*mcsarr)[midp-1].point; // 把h的值和取的两个值point值进行比较,若在这两个point值之间说明h值应该放在较大的那个point值的下标对应的ip地址上 if ( h <= midval && h > midval1 ) return &( (*mcsarr)[midp] );  if ( midval < h ) // 否则继续二分查找 lowp = midp + 1; else highp = midp - 1;  if ( lowp > highp ) // 若没有找到,直接返回0位置的值,这种情况应该很少 return &( (*mcsarr)[0] ); } }
  查找服务器节点如下:
  1、根据请求的key进行hash得到一个整数。
  2、根据该整数从环上查找到一个比该整数大最近虚拟机节点,获取到的服务器节点就是要找的服务器。
  以上就是hash一致性算法的实现。
  优点 :具有较好的扩展性和容错性,同时保证了数据的平衡性。
  缺点 :当某个客户端发送很多请求,后端hash到同一个服务器节点,会导致服务器负载过大,也即是没有解决热点问题。
  总结
  负载均衡就是通过一定方式把客户端的请求分配到相应的服务节点上,保证各个服务器节点能够在自己的承载范围内提供服务。 负载均衡分为硬件负载均衡和软件负载均衡,一般接触到最多的是软件负载均衡,软件负载均衡有不同的负载均衡算法,各个负载均衡算法有各自的优缺点,可以根据适当的场景采用对应的算法。

AMD新一代显卡降价明显,一线品牌RX7900XT已降至6799元过完年之后,AMD新一代显卡降价明显,其中不少一线品牌的RX7900XT已降至6799元,值得关注。第1款推荐的是撼讯(PowerColor)AMDRADEONRX7900XT暗黑天空新闻巴黎奥组委承诺绝不让欧冠决赛球迷骚乱悲剧重演中国小康网02月15日讯老马奥运负责人告诉天空新闻,他们已得到保证,一份新报告中的建议将在明年巴黎奥运会之前在场馆实施。去年欧冠赛期间球迷强闯赛场与警察对峙英国天空新闻台报道,去年ChatGPT被多方抢注商标!海外商标抢注频现,如何防范?最近,全世界都在讨论ChatGPT,跨境圈也是如此。这个文武双全,能写诗歌论文能写代码改BUG会聊天会道歉还会学习,智商情商双高的AI机器人ChatGPT横空出世后,火爆全球,相关所有生命都起源于一个古老的细胞吗?生命真的起源于外星陨石么?当我们审视当今地球上存在的各种生命形式时,从最简单的细菌到最大最复杂和分化的植物动物和真菌,我们发现一切都包含相同的基本组成部分细胞。当我们翻阅化石记录并重建地球上的生命史时,越走一加新机开售,红米放出王炸应战!这波国产机开打真精彩每年三月份,手机市场都会掀起一场腥风血雨的打斗,宣发新机抢夺用户市场。不过今年的国产手机大乱斗来的有点早,特别是在20003000价位的中端手机中,前有真我发布GTNeo5直接对标2023首届全国人工智能应用场景创新挑战赛正式启动2月14日下午,2023首届全国人工智能应用场景创新挑战赛正式启动。本届大赛由中国人工智能学会科技部新一代人工智能发展研究中心联合主办,以场景驱动数智强国为主题,旨在以场景创新驱动视频中为什么需要这么多的颜色空间?导读在视频处理中,我们经常会用到不同的色彩空间非线性RGB,线性RGB,YUV,XYZ为什么需要这么多的色彩空间呢?为什么在FFMpeg中会有colorspace,colortrawindows上好用的视频录制工具当今,人们经常需要在电脑上进行视频录制,比如录制游戏,制作教学视频等等。对于Windows系统用户来说,选择一个好用的视频录制工具非常重要。以下是一些值得推荐的Windows上的视危难时逆行运营上应变空运突击队守护供应链稳定。连夜运输进博会参展商品。过去三年对整个物流行业来说都极不寻常,来势汹汹的新冠疫情,给供应链行业带来了巨大的冲击。特别是2022年的上海,物流公司更是受到了oppo手机容易忽略的电量设置细节手机是oppok7,已经使用了二年多了,随着使用时间增长感觉电量有点不耐用了。手机流畅度如不玩大型游戏还是比较流畅的。假设在硬件没问题的时候,有时简单设置一下,手机电量会更耐用点。大结局的剧情走向,看完这个细节你就懂了!影视狂飙连载三盘点狂飙中隐藏很深的细节细节十三安欣的命运暗示第15集里安欣说过这样的话从第一集中年安欣填的表来看他真的跟高启强耗了一辈子终身未婚细节十四李响的结局21集中安欣和李响发生争执导致车
第40届中国洛阳牡丹文化节最佳攻略云游风物之旅第40届中国洛阳牡丹文化节正在向我们走来,小编整理了一些相关消息开幕式时间确定开幕式时间4月8日晚地点牡丹阁景区南广场开幕式时长40分钟,共设计花都秀水都游王都传诗都赋健身赏景两不误北京冬奥公园成京西健身休闲好去处京报体育记者卓然北京冬奥公园位于石景山区西部永定河沿岸。这座城市休闲公园将首钢园及京西多座公园串联起来,形成自然与钢城的完美结合。在公园里,市民可以运动健身参与亲子活动,也能沿着永杯酒释兵权是北宋重文抑武的开端自古以来,开国皇帝在打下江山后,都喜欢先向功臣下手,越是具有军事才能的武将,就越是危险。比如韩信,与张良萧何同为汉初三杰,却只有他被残忍杀害,另外,彭越和英布两位名将也被刘邦所杀。吉林县市历史梨树梨树简述从境内长山遗址考证,上溯至新石器时代梨树已有人类繁衍生息,肃慎夫余高句丽鲜卑契丹女真蒙古等北方少数民族迭居。辽金时代属韩州。治所曾多次迁徙。1150年(天徳二年)州治所从柳1953年北京一妇女病逝,去世前向丈夫坦白我曾是大清皇妃在1953年9月17日,本是平常的一天,北京城内即将迎来秋风萧瑟。这时在辟才胡同西口的一个破旧四合院内,有一位叫做傅玉芳的女人即将走完她的一生,这年她才四十四岁。只见最后一刻,她对著史并称3南阳五圣诸葛亮等,竟然是颇有争议的营销噱头臣本布衣,躬耕于南阳出师表诸葛亮出师表是初中就熟读背诵的文章。这里的南阳,就是本文主要讨论的河南省西南部的城市。大家知道南阳五圣吗,知道文题所指此称谓为营销噱头的含义吗?就让小编来听信谗言赵氏孤儿的始作俑者最终落得栽进粪坑呛死晋景公(?出生不详公元前581年),姬姓,名獳,春秋时期晋国第二十六代君主(公元前599年前581年在位),晋文公之孙晋成公之子,于公元前599年继其父晋成公之位。一生好战,发动了人民政协一届会议中,有这样一个发出过我没有对不起祖国的人时值2023年全国政协十四届一次会议开幕,我们就来聊一聊,中国人民政治协商会议第一届全体会议中这样一个人物。他曾任中央音乐学院院长,中国音乐家协会会长等职业,没错,他就是以文艺界代明朝与朝鲜的和亲外交贡女制度在这两年讲述明朝的热播剧中,不少人惊讶地发现明成祖朱棣的后宫中有不少来自朝鲜的嫔妃。甚至民间的野史传闻里,朱棣的生母也是朝鲜人,似乎大明皇帝对朝鲜女子都颇为喜爱。不过,有关朱棣生母古代不孝罪有多严苛?大义灭亲另立门户不守孝都要被判刑孝悌从古至今,都是社会讨论的焦点之一。相对现在的,温和,且多从道德层面而言,古时的人们对这个问题更加严苛,甚至有专门的法律惩罚来规定人们尽孝。孝与一个人的社会风评,邻里关系,以及前山东菏泽行政区划变迁,5个县陆续划出,如今只有7个县!山东省的菏泽市如今名气不大,经济发展在整个山东并不显眼,但历史上曾经是搅动风云的兵家必争之地,名人辈出。孙膑,吴起,伯乐,吕后,彭越,李典,黄巢,宋江诸多历史人物都出自菏泽这片土地