软件开发报告总结归纳.docx
《软件开发报告总结归纳.docx》由会员分享,可在线阅读,更多相关《软件开发报告总结归纳.docx(38页珍藏版)》请在冰豆网上搜索。
![软件开发报告总结归纳.docx](https://file1.bdocx.com/fileroot1/2023-7/14/a673b511-0967-496a-b679-1f6641f3921b/a673b511-0967-496a-b679-1f6641f3921b1.gif)
软件开发报告总结归纳
负载均衡器软件
开发报告
院系:
班级:
成员:
1.可行性分析报告
1.1项目背景
面对庞大的数据流量,面对集中性的访问,是不是觉得网络服务器岌岌可危呢?
不用怕,负载均衡器就能帮你缓解这个问题。
负载均衡器通过虚拟IP地址方法,解决了轮流排程所面临的许多问题。
使用了负载均衡器集群系统,在外部看来,像是具有一个IP地址的单一服务器一样,当然,这个IP地址是虚拟的,它映射了集群中的每一台机器的地址。
所以,在某种程度上,负载均衡器是将整个集群的IP地址报漏给外部网络。
当请求到达负载均衡器时,它会重写该请求的头文件,并将之指定到集群中的机器上。
如果某台机器被从集群中移除了,请求不会别发往已经不存在的服务器上,因为所有的机器表面上都具有同一个IP地址,即使集群中的某个节点被移除了,该地址也不会发生变化。
而且,internet上缓存的DNS条目也不再是问题了。
当返回一个应答时,客户端看到的只是从负载均衡器上所返回的结果。
也就是说,客户端操作的对象是负载均衡器,对于其更后端的操作,对客户端来讲,是完全透明的。
1.2产品分析
服务器一致性负载均衡器读取客户端发出的每一个请求中所包含的cookies或url解释。
基于所读出的这些信息,负载均衡器就可以重写报头并将请求发往集群中合适的节点上,该节点维护着相应客户端请求的会话信息。
在HTTP通信中,负载均衡器可以提供服务器一致性,但并不是通过一个安全的途径(例如:
HTTPS来提供这种服务。
当消息被加密后(SSL,负载均衡器就不能读出隐藏在其中的会话信息。
通过故障恢复机制获得高可靠性故障恢复发生在当集群中某个节点不能处理请求,需将请求重新导向到其他节点时。
主要有两种故障恢复:
请求级故障恢复。
当集群中的一个节点不能处理请求时(通常是由于down机),请求被发送到其他节点。
当然,在导向到其他节点的同时,保存在原节点上的会话信息将会丢失。
透明会话故障恢复。
当一个引用失败后,负载均衡器会将之发送到集群中其他的节点上,以完成操作,这一点对用户来说是透明的。
由于透明会话故障恢复需要节点具备相应的操作信息,因此为了实现该功能,集群中的所有节点必须具有公共存储区域或通用数据库,存储会话信息数据,以提供每个节点在进行单独进程会话故障恢复时所需要的操作信息。
既然所有的Web应用请求都必须经过负载均衡系统,那么系统就可以确定活动会话的数量,在任何实例访问中的活动会话的数目,应答的次数,高峰负载次数,以及在高峰期和低谷期的会话的数目,还有其他更多的。
所有的这些统计信息都可以被很好的用来调整整个系统的性能。
1.3结论意见
虽然此项目存在一定的技术难度和风险,但我们对项目要达到的目标十分清楚,对所要开发系统将要实现的功能也非常了解。
而且有一些成品作为参考,并且在项目的实施过程中我们能够获得帮助,我认为只要我们能够认真思考、仔细规划、明确分工,我们可以承担此项目的开发。
2.项目开发计划
2.1总体功能要求
1.扩展和的带宽
2•增加
3.加强网络数据处理能力
4.提高网络的灵活性和可用性
2.2软件开发平台要求
VisualC++6.0
SQLServer2008
C++Builder
网络架构:
完全支持TCP/IP协议
2.3软件项目的开发实施过程管理要求
3.软件开发
3.1软件的需求分析
1.DNS负载均衡最早的负载均衡技术是通过DNS来实现的,在DNS^为多个地址配置同一个名字,因而查询这个名字的客户机将得到其中一个地址,从而使
得不同的客户访问不同的服务器,达到负载均衡的目的。
DNS负载均衡是一种简
单而有效的方法,但是它不能区分服务器的差异,也不能反映服务器的当前运行状态。
2•代理服务器负载均衡使用代理服务器,可以将请求转发给内部的服务
器,使用这种加速模式显然可以提升静态网页的访问速度。
然而,也可以考虑这
样一种技术,使用代理服务器将请求均匀转发给多台服务器,从而达到负载均衡
的目的。
3•地址转换网关负载均衡支持负载均衡的地址转换网关,可以将一个外
部IP地址映射为多个内部IP地址,对每次TCP连接请求动态使用其中一个内部地址,达到负载均衡的目的。
4.协议内部支持负载均衡除了这三种负载均衡方式之外,有的协议内部支
持与负载均衡相关的功能,例如HTTP协议中的重定向能力等,HTTP运行于TCP连接的最高层。
5.NAT负载均衡NAT简单地说就是将一个IP地址转换为另一个IP地址,一
般用于未经注册的内部地址与合法的、已获注册的InternetIP地址间进行转
换。
适用于解决InternetIP地址紧张、不想让网络外部知道内部网络结构等
的场合下。
6.反向代理负载均衡普通代理方式是代理内部网络用户访问internet上服
务器的连接请求,客户端必须指定代理服务器,并将本来要直接发送到internet上服务器的连接请求发送给代理服务器处理。
反向代理(ReverseProxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。
反向代理负载均衡技术是把将来自internet上的连接请求以反向代理的方式动态地转发给内部网络上的多台服务
器进行处理,从而达到负载均衡的目的。
7•混合型负载均衡在有些大型网络,由于多个服务器群内硬件设备、各自的规模、提供的服务等的差异,可以考虑给每个服务器群采用最合适的负载均衡方式,然后又在这多个服务器群间再一次负载均衡或群集起来以一个整体向外界提供服务(即把这多个服务器群当做一个新的服务器群),从而达到最佳的性能。
将这种方式称之为混合型负载均衡。
此种方式有时也用于单台均衡设备的性能不能满足大量连接请求的情况下。
3.2软件的概要设计
软件负载均衡解决方案是指在一台或多台服务器相应的操作系统上安装一
个或多个附加软件来实现负载均衡,DNSLoadBalanceCheckPointFirewall-1
ConnectControl等,它的优点是基于特定环境,配置简单,使用灵活,成本低廉,可以满足一般的负载均衡需求。
硬件负载均衡解决方案是直接在服务器和外部网络间安装负载均衡设备,这种设备通常称之为负载均衡器,由于专门的设备完成专门的任务,独立于操作系统,整体性能得到大量提高,加上多样化的负载均衡策略,智能化的流量管理,可达到最佳的负载均衡需求。
一般而言,硬件负载均衡在功能、性能上优于软件方式,不过成本昂贵。
本地负载均衡能有效地解决数据流量过大、网络负荷过重的问题,并且不需花费
昂贵开支购置性能卓越的服务器,充分利用现有设备,避免服务器单点故障造成数据流量的损失。
其有灵活多样的均衡策略把数据流量合理地分配给服务器群内的服务器共同负担。
即使是再给现有服务器扩充升级,也只是简单地增加一个新的服务器到服务群中,而不需改变现有网络结构、停止现有的服务。
全局负载均衡主要用于在一个多区域拥有自己服务器的站点,为了使全球用
户只以一个IP地址或域名就能访问到离自己最近的服务器,从而获得最快的访问速度,也可用于子公司分散站点分布广的大公司通过企业内部互联网来达到资源统一合理分配的目的。
3.3软件的详细设计
1.轮转法:
轮转算法是所有调度算法中最简单也最容易实现的一种方法。
在
一个任务队列里,队列的每个成员(节点)都具有相同的地位,轮转法简单的在这组成员中顺序轮转选择。
在负载平衡环境中,均衡器将新的请求轮流发给节点队列中的下一节点,如此连续、周而复始,每个集群的节点都在相等的地位下被轮流选择。
这个算法在DNS域名轮询中被广泛使用。
轮转法的活动是可预知的,每个节点被选择的机会是1/N,因此很容易
计算出节点的负载分布。
轮转法典型的适用于集群中所有节点的处理能力和性能均相同的情况,在实际应用中,一般将它与其他简单方法联合使用时比较有效。
2.散列法:
散列法也叫哈希法(HASH,通过单射不可逆的HASH函数,按照某种规则将网络请求发往集群节点。
哈希法在其他几类平衡算法不是很有效时会显示出特别的威力。
例如,在前面提到的UDP会话的情况下,由于轮转法和其他几类基于连接信息的算法,无法识别出会话的起止标记,会引起应用混乱。
而采取基于数据包源地址的哈希映射可以在一定程度上解决这个问题:
将具有相同源地址的数据包发给同一服务器节点,这使得基于高层会话的事务可以以适当的方式运行。
相对称的是,基于目的地址的哈希调度算法可以用在Web
Cache集群中,指向同一个目标站点的访问请求都被负载平衡器发送到同一个Cache服务节点上,以避免页面缺失而带来的更新Cache问题。
3.最少连接法:
在最少连接法中,平衡器纪录目前所有活跃连接,把下一个新的请求发给当前含有最少连接数的节点。
这种算法针对TCP连接进行,但由于不同应用对系统资源的消耗可能差异很大,而连接数无法反映出真实的应用负
载,因此在使用重型Web服务器作为集群节点服务时(例如Apache服务器),该算法在平衡负载的效果上要打个折扣。
为了减少这个不利的影响,可以对每个节点设置最大的连接数上限(通过阈值设定体现)。
4.最低缺失法:
在最低缺失法中,平衡器长期纪录到各节点的请求情况,把下个请求发给历史上处理请求最少的节点。
与最少连接法不同的是,最低缺失记录过去的连接数而不是当前的连接数。
5.最快响应法:
平衡器记录自身到每一个集群节点的网络响应时间,并将下一个到达的连接请求分配给响应时间最短的节点,这种方法要求使用ICMP包或基于UDP包的专用技术来主动探测各节点。
在大多数基于LAN的集群中,最快响应算法工作的并不是很好,因为LAN中的ICMP包基本上都在10ms内完成回应,体现不出节点之间的差异;如果在WAN上进行平衡的话,响应时间对于用户就近选择服务器而言还是具有现实意义的;而且集群的拓扑越分散这种方法越能体现出效果来。
这种方法是高级平
衡基于拓扑结构重定向用到的主要方法。
6.加权法:
加权方法只能与其他方法合用,是它们的一个很好的补充。
加权
算法根据节点的优先级或当前的负载状况(即权值)来构成负载平衡的多优先级队列,队列中的每个等待处理的连接都具有相同处理等级,这样在同一个队列里
可以按照前面的轮转法或者最少连接法进行均衡,而队列之间按照优先级的先后顺序进行均衡处理。
在这里权值是基于各节点能力的一个估计值。
3.4软件的编码
#ineludevexception>
#includeverrno.h>
#include
#inelude"conn.h"
#include"log.h"
#include"fdwrapper.h"
conn:
:
conn()
{
m_srvfd=-1;
m_clt_buf=newchar[BUF_SIZE];
if(!
m_clt_buf)
{
throwstd:
:
exception();
}
m_srv_buf=newchar[BUF_SIZE];
if(!
m_srv_buf)
{
throwstd:
:
exception();
}
reset();
}
conn:
:
~conn()
{
delete[]m_clt_buf;
delete[]m_srv_buf;
}
voidconn:
:
init_clt(intsockfd,constsockaddr_in&client_addr)
{
m_cltfd=sockfd;
m_clt_address=client_addr;
}
voidconn:
:
init_srv(intsockfd,constsockaddr_in&server_addr)m_srvfd=sockfd;m_srv_address=server_addr;
}
voidconn:
:
reset()
{
m_clt_read_idx=0;
m_clt_write_idx=0;
m_srv_read_idx=0;
m_srv_write_idx=0;
m_srv_closed=false;
m_cltfd=-1;
memset(m_clt_buf,'\0',BUF_SIZE);
memset(m_srv_buf,'\0',BUF_SIZE);
}
RET_CODEconn:
:
read_clt()
{
intbytes_read=0;
while(true)
{
if(m_clt_read_idx>=BUF_SIZE)
{
read
log(LOG_ERF__FILE_,__LINE__,"%s","theclient
bufferisfull,letserverwrite");
returnBUFFER_FULL;
}
bytes_read=recv(m_cltfd,m_clt_buf+m_clt_read」dx,
BUF_SIZE-m_clt_read_idx,0);
if(bytes_read==-1)
{
if(errno==EAGAIN||errno==EWOULDBLOCK)
{
break;
}
returnIOERR;
elseif(bytes_read==0)
{
returnCLOSED;
}
m_clt_read_idx+=bytes_read;
}
return((m_clt_read」dx-m_clt_write_idx)>0)?
OK:
NOTHING;}
RET_CODEconn:
:
read_srv()
{
intbytes_read=0;
while(true)
{
if(m_srv_read_idx>=BUF_SIZE)
{
log(LOG_ERF__FILE_,__LINE__,"%s","theserverread
bufferisfull,letclientwrite");
returnBUFFER_FULL;
}
bytes_read=recv(m_srvfd,m_srv_buf+m_srv_read」dx,
BUF_SIZE-m_srv_read_idx,0);
if(bytes_read==-1)
{
if(errno==EAGAIN||errno==EWOULDBLOCK)
{
break;
}
returnIOERR;
}
elseif(bytes_read==0)
{
log(LOG_ERR,__FILE__,__LINE__,"%s","theserver
shouldnotclosethepersistconnection”);
returnCLOSED;
m_srv_read_idx+=bytes_read;
}
return((m_srv_read_idx-m_srv_write_idx)>0)?
OK:
NOTHING;}
RET_CODEconn:
:
write_srv()
{
intbytes_write=0;
while(true)
{
if(m_clt_read_idx<=m_clt_write_idx)
{
m_clt_read_idx=0;
m_clt_write_idx=0;
returnBUFFER_EMPTY;
}
bytes_write=send(m_srvfd,m_clt_buf+m_clt_write_idx,
m_clt_read_idx-m_clt_write_idx,0);
if(bytes_write==-1)
{
if(errno==EAGAIN||errno==EWOULDBLOCK)
{
returnTRY_AGAIN;
}
log(LOG_ERR,__FILE__,__LINE__,"writeserversocket
failed,%s",strerror(errno));
returnIOERR;
}
elseif(bytes_write==0)
{
returnCLOSED;
}m_clt_write_idx+=bytes_write;
RET_CODEconn:
:
write_clt()
{
intbytes_write=0;
while(true)
{
if(m_srv_read_idx<=m_srv_write_idx)
{
m_srv_read_idx=0;
m_srv_write_idx=0;
returnBUFFER_EMPTY;
}
bytes_write=send(m_cltfd,m_srv_buf+m_srv_write_idx,
m_srv_read_idx-m_srv_write_idx,0);
if(bytes_write==-1)
{
if(errno==EAGAIN||errno==EWOULDBLOCK)
{
returnTRY_AGAIN;
}
log(LOG_ERR,_FILE_,__LINE__,"writeclientsocket
failed,%s",strerror(errno));
returnIOERR;
}
elseif(bytes_write==0)
{
returnCLOSED;
}
m_srv_write_idx+=bytes_write;
}
}
#ifndefFDWRAPPER_H
#defineFDWRAPPER_H
#include
#ineludevfentl.h>
#include
intsetnonblocking(intfd)
{
intold_option=fcntl(fd,F_GETFL);
intnew_option=old_option|O_NONBLOCK;
fcntl(fd,F_SETFL,new_option);
returnold_option;
}
voidadd_read_fd(intepollfd,intfd)
{
epoll_eventevent;
event.data.fd=fd;
event.events=EPOLLIN|EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);setnonblocking(fd);
}
voidadd_write_fd(intepollfd,intfd)
{
epoll_eventevent;
event.data.fd=fd;
event.events=EPOLLOUT|EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);setnonblocking(fd);
}voidclosefd(intepollfd,intfd)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,0);
close(fd);
}
voidremovefd(intepollfd,intfd)
{
epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,0);
}
voidmodfd(intepollfd,intfd,intev)
{
epoll_eventevent;
event.data.fd=fd;
event.events=ev|EPOLLET;
epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&event);
}
#endif
#include
#include
#include
#include"log.h"staticintlevel=LOG_INFO;
staticintLOG_BUFFER_SIZE=2048;
staticconstchar*loglevels[]=
{
"emerge!
","alert!
","critical!
","info:
","debug:
"
};
voidset_loglevel(intlog」evel){
level=log_level
}voidlog(intlog」evel,constchar*file_name,intline_num,constchar*format,...)
{
if(log」evel>level)
{
return;
}
time_ttmp=time(NULL);
structtm*cur_time=localtime(&tmp);
if(!
cur_time)
{
return;
}
chararg_buffer[LOG_BUFFER_SIZE];
cur_time);
memset(arg_buffer,'\0',LOG_BUFFER_SIZE);
strftime(arg_buffer,LOG_BUFFER_SIZE1,"[%x%X]"printf("%s",arg_buffer);
printf("%s:
%04d",file_name,line_num);
printf("%s",loglevels[log_level-LOG_EMERG]);
va_listarg」ist;
va_start(arg」ist,format);
memset(arg_buffer,'\0',LOG_BUFFER_SIZE);
vsnprintf(arg_buffer,LOG_BUFFER_SIZE-1,format,arg_list);
printf("%s\n",arg_buffer);
fflush(stdout);
va_end(arg_list);
}
#include#include#include