http://www.javabloger.com/article/ngixn-j2ee-tomcat-memcache-session-share.html
posted @ 2012-02-17 16:04 attitudedecidesall Views(27) Comments(0) Edit memcached session一、需求背景
一般的 servlet 容器(如 tomcat, resin 等)实现的 javax.servlet.http.HttpSession 的可见性都仅局限于一个 web 应用程序内,即使运行在同一个 JVM 里两个不同的 web 应用之间也不可共享 session 信息。通常情况下,我们可以考虑把会话状态信息保存在数据库中或者构建一个分布式 session 服务。 Memcached-session-0.9.jar (命名为 0.9 的原因是没有经过全面测试,但基本够用)是利用memcached 作为 session 服务器构建的一个分布式可共享 sesssion 解决方案。 Memcached-session 中的信息能够被多个共享*域名的 web 应用所共享(注:由于 memcached-session 的 sessionid 也是借助于 cookie 的,为防止 cookie 跨域的问题,必须要求各个 web 应用在相同的*域名下。 对于,要跨*域名的多个 web 应用程序共享 session 的问题,需要解决 cookie 跨域的问题,通常的做法是通过重定向的方式让 sessionid 在第三方的一个域上生成,详细情况可参考《 SSO(UserID 方式 )SP 与 SSO 之间的通信接口 V0.1.doc 》设计文档)。
二、使用步骤
( 1 )安装 memcached 服务端并启动在默认端口 11211 ,可以选择 windows 或 linux 版本
注:所有的会话信息都以 SessionID 到 SessionObject (本身也是个类似 Map 的数据结构)键值对形式保存在 memcached 服务器端,各个 web 应用程序再通过 memcached 客户端来访问。这一点跟信息保存在数据库服务器上,各应用程序再通过 SQL 客户端来访问是一个道理。
( 2 )获取 memcached-session-0.9.jar 包,将该包及其依赖的 java_memcached_release_1.5.jar 和 commons-logging-1.0.4.jar 导入 WEB-INF/lib 目录下
注: memcached-session-0.9.jar 依赖于 java_memcached_release_1.5.jar ( memcached 服务器的 java 客户端)和commons-logging-1.0.4.jar ( apache 的日志组件)
( 3 )编辑 memcached.properties 配置文件,以保证 memcached 客户端能够正确地访问 memcached 服务端。
注: memcached-session-0.9.jar 是依据 classpath 去寻找 memcached.properties 配置文件的,所以必须保证memcached.properties 配置文件在 classpath 的根路径下,而且名称必须是 memcached.properties 。
样例
servers=127.0.0.1:11211,10.10.41.186:11211 (多个服务器之间用“ , ”隔开)
init-connection = 10
min-connection = 5
max-connection = 20
maint-sleep-millis = 30
active-check = true
failover = true
nagle = false
( 4 )在 web.xml 中配置 MemcachedFilter ,以替换 sevlet 容器自有的 HttpSession 实现。
<filter>
<display-name>MemcachedFilter</display-name>
<filter-name>MemcachedFilter</filter-name>
<filter-class>com.umpay.session.MemcachedFilter<!-- [if !supportAnnotations]-->[lw1] <!-- [endif]--> </filter-class>
<init-param>
<param-name>uuidCookieName <!-- [if !supportAnnotations]-->[lw2] <!-- [endif]--> </param-name>
<param-value>uuid</param-value>
</init-param>
<init-param>
<param-name>uuidCookieDomain</param-name>
<param-value></param-value>
</init-param>
<init-param>
<param-name>uuidCookiePath</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>uuidCookieMaxAge <!-- [if !supportAnnotations]-->[lw3] <!-- [endif]--> </param-name>
<param-value>-1</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>MemcachedFilter</filter-name>
<url-pattern>/* <!-- [if !supportAnnotations]-->[lw4] <!-- [endif]--> </url-pattern>
</filter-mapping>
( 5 )透明地使用 MemcachedSession
protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
int c = 1;
String count = (String)request.getSession() . <!-- [if !supportAnnotations]-->[lw5] <!-- [endif]--> getAttribute(FLAG );
if (count != null ) {
c = Integer.parseInt (count)+1;
}
request.getSession().setAttribute( FLAG , c+ "" );
request.getSession().setAttribute( "LastIP" , request.getRemoteAddr());
PrintWriter writer = response.getWriter();
request.getSession().setMaxInactiveInterval(8);
writer.write( "MaxInactiveInterval = " + request.getSession().getMaxInactiveInterval()+ " second" );writer.println();
writer.write( "you accessed this page at time " + c);writer.println();
writer.write( "SESSIONID = " + request.getSession().getId());writer.println();
writer.println();
Enumeration<String> attrs = request.getSession().getAttributeNames();
while (attrs.hasMoreElements()) {
String key = attrs.nextElement();
writer.write( "AttrEnum ->" +key+ "=" +request.getSession().getAttribute(key));
writer.println();
}
writer.println();
writer.write( "CreationTime = " + new Date(request.getSession().getCreationTime()));
writer.println();
writer.write( "LastAccessedTime = " + new Date(request.getSession().getLastAccessedTime()));
writer.println();
}
三、报文分析
( 1 )初次访问时
请求报文:
(Request-Line) GET /MemSession/counter HTTP/1.1
Host localhost:8080
User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language zh-cn,zh;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset gb2312,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
Connection keep-alive
Cache-Control max-age=0
响应报文:
(Status-Line) HTTP/1.1 200 OK
Server Apache-Coyote/1.1
Set-Cookie uuid=532b 3c 0b-c78e-4c2f-81fa-295ab9a57acf; Domain=""; Path=/<!-- [if !supportAnnotations]-->[lw6] <!-- [endif]-->
Set-Cookie JSESSIONID=2560E054058B509381421BB017D 8A 418; Path=/MemSession <!-- [if !supportAnnotations]-->[lw7] <!-- [endif]-->
Content-Length 272
Date Fri, 19 Jun 2009 05:22:02 GMT
(响应包体内容)
MaxInactiveInterval = 8 second
you accessed this page at time 1
SESSIONID = 532b3c0b-c78e-4c2f-81fa-295ab9a57acf
AttrEnum ->LastIP=127.0.0.1
AttrEnum ->SessionCounter=1
CreationTime = Fri Jun 19 13:22:11 CST 2009
LastAccessedTime = Fri Jun 19 13:22:14 CST 2009
( 2 )再次访问时
请求报文:
(Request-Line) GET /MemSession/counter HTTP/1.1
Host localhost:8080
User-Agent Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language zh-cn,zh;q=0.5
Accept-Encoding gzip,deflate
Accept-Charset gb2312,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
Connection keep-alive
Cookie JSESSIONID=2560E054058B509381421BB017D8A418; uuid=532b 3c 0b-c78e-4c2f-81fa-295ab9a57acf <!-- [if !supportAnnotations]-->[lw8] <!-- [endif]-->
Cache-Control max-age=0
响应报文:
(Status-Line) HTTP/1.1 200 OK
Server Apache-Coyote/1.1
Content-Length 272
Date Fri, 19 Jun 2009 05:22:11 GMT
(响应包体内容)
MaxInactiveInterval = 8 second
you accessed this page at time 2
SESSIONID = 532b3c0b-c78e-4c2f-81fa-295ab9a57acf
AttrEnum ->LastIP=127.0.0.1
AttrEnum ->SessionCounter=2
CreationTime = Fri Jun 19 13:22:11 CST 2009
LastAccessedTime = Fri Jun 19 13:22:14 CST 2009
四、 FAQ
( 1 ) web 应用程序重启, MemcachedSession 信息会不会丢失呀?
答:不会, MemcachedSession 信息是保存在 Memcached 服务端的,各个 web 应用只是通过 memcached-session-0.9.jar 去访问 Memcached 服务端。只有 Memcached 服务端重启时,信息才会丢失。
( 2 )服务端 MemcachedSession 的过期时间如何设置?
答:在 web.xml 的 com.umpay.session.MemcachedFilter 的 initParam 初始化配置时通过
<init-param>
<param-name>uuidCookieMaxAge</param-name>
<param-value>-1</param-value>
</init-param>
可以配置,该配置对所有客户端生效;
也可以对某个浏览器专门使用 request.getSession().setMaxInactiveInterval(8); 来设置特定的超时时间。在实现机制上, memcached-session-0.9.jar 会在从 memcached 服务端取回数据时,依据系统当前时间 - 最后一次访问时间来判断 MemcachedSession 是否过期,如果过期,此时才删除服务端的数据。也就是说只有当数据已经过期,并且浏览器触发该数据的访问时 MemcachedSession 才会从服务端真的删除,否则只有让 memcached 自己删除。
<!-- [if !supportAnnotations]--><!-- [endif]--><!-- [if !supportAnnotations]--><!-- [endif]--><!-- [if !supportAnnotations]--> <!-- [endif]-->
<!-- [if !supportAnnotations]-->[lw1] <!-- [endif]--> Memcached-session-0.9.jar 中的组件,并在 init-param 中配置该组件的初始化参数
< !-- [if !supportAnnotations]-->< !-- [endif]--><!-- [if !supportAnnotations]--><!-- [endif]--><!-- [if !supportAnnotations]--> <!-- [endif]--><!-- [if !supportAnnotations]-->[lw2] <!-- [endif]--> memcachedSession 的 sessionid 也是基于 cookie 的,且 sessionid 的生成规则是UUID 。
< !-- [if !supportAnnotations]-->< !-- [endif]--><!-- [if !supportAnnotations]--><!-- [endif]--><!-- [if !supportAnnotations]--> <!-- [endif]--><!-- [if !supportAnnotations]-->[lw3] <!-- [endif]--> 指定服务端 sessionid 失效时间,其中 >0 表示在指定时间(秒)后失效; =0 表示永远不失效; <0 默认 30 分钟后失效。
< !-- [if !supportAnnotations]-->< !-- [endif]--><!-- [if !supportAnnotations]--><!-- [endif]--><!-- [if !supportAnnotations]--> <!-- [endif]--><!-- [if !supportAnnotations]-->[lw4] <!-- [endif]--> 指定对哪些 url 请求的自有 HttpSession 被 MemcachedSession 替换掉。
< !-- [if !supportAnnotations]-->< !-- [endif]--><!-- [if !supportAnnotations]--><!-- [endif]--><!-- [if !supportAnnotations]--> <!-- [endif]--><!-- [if !supportAnnotations]-->[lw5] <!-- [endif]--> 获取的 Session 实现实例实际是 com.umpay.session.MemcachedSession ,采用的是装饰器模式给偷梁换柱了。
< !-- [if !supportAnnotations]-->< !-- [endif]--><!-- [if !supportAnnotations]--><!-- [endif]--><!-- [if !supportAnnotations]--> <!-- [endif]--><!-- [if !supportAnnotations]-->[lw6] <!-- [endif]--> 用户自己定义的 MemcachedSession 对应的 uuid
< !-- [if !supportAnnotations]-->< !-- [endif]--><!-- [if !supportAnnotations]--><!-- [endif]--><!-- [if !supportAnnotations]--> <!-- [endif]--><!-- [if !supportAnnotations]-->[lw7] <!-- [endif]--> Servlet 容器自有的 SessionID
< !-- [if !supportAnnotations]-->< !-- [endif]--><!-- [if !supportAnnotations]--><!-- [endif]--><!-- [if !supportAnnotations]--> <!-- [endif]--><!-- [if !supportAnnotations]-->[lw8] <!-- [endif]--> 浏览器在再次访问同一域下的 URL 时,主动把先前下发的 uuid 的 cookie 信息包裹在请求报文中,以便服务端依据该 ID 匹配 MemcachedSession 对象。
< !-- [if !supportAnnotations]-->< !-- [endif]-->- MemSession.rar (419.1 KB)
- 下载次数: 55
- SSO_UserID方式_SP与SSO之间的通信接口V0.1.pdf (188.3 KB)
- 下载次数: 31
- memcached-session使用说明V0.1.pdf (304.2 KB)
- 下载次数: 47
- memcached全面剖析.pdf (929.5 KB)
- 下载次数: 49
要集群tomcat主要是解决SESSION共享的问题,因此我利用memcached来保存session,多台TOMCAT服务器即可共享SESSION了。
你可以自己写tomcat的扩展来保存SESSION到memcached。
这里推荐使用memcached-session-manager这个开源项目(http://code.google.com/p/memcached-session-manager/ ),下面简称msm。
如何安装nginx、memcached、tomcat这些就不多说了。
先说明一下测试环境:
tomcat1、nginx、memcached安装在192.168.1.11
tomcat2安装在192.168.1.101
下面分步实现基于nginx的tomcat负载均衡和集群配置
一,tomcat集群
1,先下载msm及其依赖包
http://memcached-session-manager.googlecode.com/files/memcached-session-manager-1.3.0.jar
http://memcached-session-manager.googlecode.com/files/msm-javolution-serializer-jodatime-1.3.0.jar
http://memcached-session-manager.googlecode.com/files/msm-javolution-serializer-cglib-1.3.0.jar
http://spymemcached.googlecode.com/files/memcached-2.4.2.jar
http://memcached-session-manager.googlecode.com/files/javolution-5.4.3.1.jar
2,将这5个包放到$TOMCAT_HOME/lib目录下
3,修改$TOMCAT_HOME/conf/server.xml
Xml代码:
<Context docBase="E:/java_codes/TestSession/WebContent" path="" reloadable="true" >
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:localhost:11211"
requestUriIgnorePattern=".*\.(png|gif|jpg|css|js)$"
sessionBackupAsync="false"
sessionBackupTimeout="100"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory"
copyCollectionsForSerialization="false"
/>
</Context> 这里的memcachedNodes是填写memcached节点,多个节点时可以以空隔分开,如:
n1:localhost:11211 n2:localhost:11212
sessionBackupTimeout的单位为分钟
E:/java_codes/TestSession/WebContent 替换成你的WEB目录
修改后重启两个TOMCAT即可,这个时候已经解决SESSION的共享问题.
posted @ 2012-02-17 14:59 attitudedecidesall Views(149) Comments(0) Edit 如何做好架构设计与写好架构设计的文档?1 建议读一下IEEE1471
2 一下是我的写文档的一些心得:
现代架构设计文档的编写
4+1 视图与 UML 软件架构设计已经逐渐成为现代软件开发过程的核心,然而能够清晰表明架构设计并不是一件容易的事,就面向对象开发而言, RUP 的 4+1 视图已在架构设计的撰写中得到了广泛的应用和认可。
对于 4+1 view 的描述有几个不同版本(或包含的视图不同,或视图的名称不同),文中以 Philippe Kruchten, November 1995 提出的 4+1 视图为准。
4+1 视图包括:逻辑视图( Logic View ),开发视图( Develop View ),进程视图( Process View ),物理视图( Physical View )和场景视图( Scenarios )。
视图间的关系
4+1 视图不仅便于我们记录架构设计,实际上它也指导了我们进行架构设计活动的部分过程。
通常我们选择 UML 来表现各种视图,以下列出了 UML 和各视图的对应关系
4+1 视图 UML
场景视图 use case
逻辑视图 类图
开发视图 类图,组件图
进程视图 无完全对应
部署视图 部署图
在架构设计稳定中通常不会给出较多的用例描述,这些是在需求稳定中定义。但是往往架构文档会选择一些用例,列入文档中,这些用例和一些非功能性需求一起用以证明架构的有效和正确性。在逻辑视图中用例的实现是必不可少的一节,尽管架构设计更关注非功能性需求。
融入 MDA 的思想 对于逻辑视图和开发视图所应包含的内容常常会觉得很难区分两者间的明显界限。逻辑视图包含更多的分析模型与实现技术本身相关性应该较少,如业务对象模型及其扩展。而开发视图则会与实现技术紧密相关。
随着 MDA 思想的推广,在架构设计文档的撰写方面也产生了影响,我们不难把 MDA 的 PIM 和逻辑视图联系起来,而把 MDA 中的 PSM 和开发视图联系起来。
在编写逻辑视图是我们应该描述与技术平台无关的模型,而开发视图则描述与实现技术平台相关的模型。
如在逻辑视图中表现的某些实体类,我们会在开发视图中转换为 EJB 组件(实体 Bean )。
这种做法不仅有利于我们编写架构设计文档,同时更是一种好的架构设计思考流程。
当今从纯网站技术上来说,因为开源模式的发展,现在建一个小网站已经很简单也很便宜,所以很多人都把创业方向定位在互联网应用。这些人里大多数不是很懂技术,或者不是那么精通,而网站开发维护方面的知识又很分散,学习成本太高,所以这篇文章将这些知识点结合起来,系统的来说,一个从日几千访问的小小网站,到日访问一两百万的小网站,中间可能会产生什么问题,以及怎么才能在一开始做足工作尽量避免这些问题。
对于不同的初期投资成本,技术路线的选择是不同的。这里假设网站刚刚只是一个构想,计划第一年服务器硬件带宽投入5万左右。对于这个资金额度,有很多种方案可选择,例如租用虚拟主机、租用单独服务器,或者流行的私有云,或者托管服务器。前两种选择,网站发展到一定规模时需迁移,那时再重做规划显然影响更大。服务器托管因为配置自主、能完全掌握控制权,所以有一定规模的网站基本都是这种模式。采用自己托管服务器的网站,一开始要注意以下几点。
一、开发语言
一般来说,技术人员(程序员)都是根据自己技术背景选择自己最熟悉的语言,不过不可能永远是一个人写程序,所以在语言的选择上还要是要费些心思。首先明确一点,无论用什么语言,最终代码质量是看管理,因此我们从前期开发成本分析。现在国内流行的适用于网站的语言,大概有java、php、.net、 python、ruby这五大阵营。python和ruby因为在国内流行的比较晚,现在人员还是相对难招一些。.net平台的人相对多,但是到后期需要解决性能问题时,对人员技能的要求比较高。剩余的java、php用人可以说是最多的。java和php无法从语言层面做比较,但对于初期,应用几乎都是靠前端支撑的网站来说,php入门简单、编写快速,优势相对大一点。至于后端例如行为分析、银行接口、异步消息处理等,等真正需要时,就要根据不同业务需求来选择不同语言了。
二、代码版本管理
稍微有点规模的网站就需要使用代码版本管理了。代码版本管理两点最大的好处,一是方便协同工作,二是有历史记录可查询比较。代码版本管理软件有很多,vss/cvs/svn/hg等,目前国内都比较流行,其中svn的普及度还是很高的。
向服务器部署代码,可以手工部署也可以自动部署。手工部署相对简单,一般可直接在服务器上svn update,或者找个新目录svn checkout,再把web root给ln -s过去。应用越复杂,部署越复杂,没有什么统一标准,只是别再用ftp上传那种形式,一是上传时文件引用不一致错误率增加,二是很容易出现开发人员的版本跟线上版本不一致,导致本来想改个错字结果变成回滚。如果有多台服务器还是建议自动部署,更换代码的机器从当前服务池中临时撤出,更新完毕后再重新加入。
三、服务器硬件
在各个机房里,靠一台服务器孤独支撑的网站数不清,但如果资金稍微充足,建议至少三台的标准配置,分别用作web处理、数据库、备份。web服务器至少要8G内存,双sata raid1,如果经济稍微宽松,或静态文件或图片多,则15k sas raid10。数据库至少16G内存,15k sas raid 10。备份服务器最好跟数据库服务器同等配置。硬件可以上整套品牌,也可以兼容机,也可以半品牌半组装,取决于经济能力。当然,这是典型的搭配,有些类型应用的性能瓶颈首先出现在web上,那种情况就要单独分析了。
web服务器可以既跑程序又当内存缓存,数据库服务器则只跑主数据库(假如是MySQL的话),备份服务器所承担就相对多一些,web配置、缓存配置、数据库配置都要跟前两台一致,这样WEB和数据库任意一台出问题,很容易就可以将备份服务器切换过去临时顶替,直到解决完问题。要注意,硬件是随时可能坏掉的,特别是硬盘,所以宁可WEB服务器跟数据库服务器放在一起,也一定不能省掉备份,备份一定要异机,并且有异步,电力故障、误操作都可能导致一台机器上的所有数据丢失。很多的开源备份方案可选择,最简单的就是rsync,写crontab里,定时同步。备份和切换,建议多做测试,选最安全最适合业务的,并且尽可能异地备份。
四、机房
三种机房尽量不要选:联通访问特别慢的电信机房、电信访问特别慢的联通机房、电信联通访问特别慢的移动或铁通机房。机房要尽可能多的实地参观,多测试,找个网络质量好,管理严格的机房。机房可以说是非常重要,直接关系到网站访问速度,网站访问速度直接关系到用户体验,访问速度很慢的网站,很难获得用户青睐。
五、架构
在大方向上,被熟知的架构是web负载均衡+数据库主从+缓存+分布式存储+队列。在一开始,按照可扩展的原则设计和编程就可以。只是要多考虑缓存失效时的雪崩效应、主从同步的数据一致性和时间差、队列的稳定性和失败后的重试策略、文件存储的效率和备份方式等等意外情况。缓存失效、数据库复制中断、队列写入错误、电源损坏,在实际运维中经常发生,如果不注意这些,出现问题时恢复期可能会超出预期很长时间。
六、服务器软件
操作系统Linux很流行。在没有专业运维人员的情况下,应倾向于择使用的人多、社区活跃、配置方便、升级方便的发行版,例如RH系列、 debian、ubuntu server等,硬件和操作系统要一起选择,看是否有适合的驱动,如果确定用某种商业软件或解决方案,也要提前知晓其对哪种操作系统支持最佳。web服务器方面,apache、nginx、lighttpd三大系列中,apache占有量还是最大,但是想把性能调教好还是需要很专业的,nginx和 lighttpd在不需要太多调整的情况下可以达到一个比较不错的性能。无论选择什么软件,除非改过这些软件或你的程序真的不兼容新版本,否则尽量版本越新越好,版本新,意味着新特性增多、BUG减少、性能增加。一个典型的php网站,基本上大多数人都没改过任何服务器软件源代码,绝大多数情况是能平稳的升级到新版本的。类似于jdk5到 jdk6,python2到python3这类变动比较大的升级还是比较少见的。看看ChangeLog,看看升级说明,结合自己情况评估测试一下,越早升级越好,升级的越晚,所花费的成本越高。对于软件包,尽量使用发行版内置的包管理工具,没有特殊要求时不建议自己编译,那样对将来运维不利。
七、数据库
几乎所有操作最后都要落到数据库身上,它又最难扩展(存储也挺难)。数据库常见的扩展方法有复制、分片,设计时要考虑到每种应用的数据如何复制、分片,当然这种考虑一般会推迟到技术设计时期。在初期进行数据库结构设计时,要根据不同的业务类型和增长量预期来考虑是否要分库、分区,并且尽量不要使用联合查询、不使用自增ID以方便分片。复制延时问题、主从数据库数据一致性问题,可以自己写或者用已有的运维工具进行检测。
用存储过程是比较难扩展的,这种情形多发生于传统C/S,特别是OA系统转换过来的开发人员。低成本网站不是一两台小型机跑一个数据库处理所有业务的模式,是机海作战。方便水平扩展比那点预分析时间和网络传输流量要重要的多的多。
另外,现在流行一种概念叫NoSQL,可以理解为非传统关系型数据库。实际应用中,网站有着越来越多的密集写操作、上亿的简单关系数据读取、热备等,这都不是传统关系数据库所擅长的,于是就产生了很多非关系型数据库,比如Redis/TC&TT/MongoDB/Memcachedb等,在测试中,这些几乎都达到了每秒至少一万次的写操作,内存型的甚至5万以上。在设计时,可根据业务特点和性能要求来选择是否使用这类数据库。例如 MongoDB,几句配置就可以组建一个复制+自动分片+failover的环境,文档化的存储也简化了传统设计库结构再开发的模式。但是当你决定采用一项技术时,一定要真正了解其优劣,例如可能你所选择的技术并不能支持你所需要的事务和数据一致性要求。
八、文件存储
存储的分布几乎跟数据库扩展一样困难,不过只有百万的PV的情况下,磁盘IO方面一般不会成大问题,一两台采用SATA做条带RAID的机器可以应付,反而是自己做异步备份比较复杂,因为小文件多。如果只有一台机器做存储,可以做简单的优化,例如放最小缩略图的分区和放中等缩略图的分区,根据平均大小调整一下块大小。存储要规划好目录结构,否则文件增多后维护起来复杂,也不利于扩展。同时还要考虑将来扩容,例如采用LVM,或者把文件根据不同规则散列到不同机器。磁盘IO繁重的情况下更容易出现故障,所以要做好备份,若发现有盘坏掉,要马上行动更换,很多人的硬盘都是坏了一块之后,接二连三的坏下去。
为了将来图片走cdn做准备,一开始最好就将图片的域名分开,且不用主域名。因为很多网站都将cookie设置到了.domain.ltd,如果图片也在这个域名下,很可能因为cookie而造成缓存失效,并且占多余流量,还可能因为浏览器并发线程限制造成访问缓慢。
九、程序
一定硬件条件下,应用能承载多少访问量,很大一部分也取决于程序如何写。程序写的不好,可能一万的访问都承载不了,写的好,可能一两台机器就能承担几百万PV。越是复杂、数据实时性要求越高的应用,优化起来越难,但对普通网站有一个统一的思路,就是尽量向前端优化、减少数据库操作、减少磁盘IO。向前端优化指的是,在不影响功能和体验的情况下,能在浏览器执行的不要在服务端执行,能在缓存服务器上直接返回的不要到应用服务器,程序能直接取得的结果不要到外部取得,本机内能取得的数据不要到远程取,内存能取到的不要到磁盘取,缓存中有的不要去数据库查询。减少数据库操作指减少更新次数、缓存结果减少查询次数、将数据库执行的操作尽可能的让你的程序完成(例如join查询),减少磁盘IO指尽量不使用文件系统作为缓存、减少读写文件次数等。程序优化永远要优化慢的部分,换语法是无法“优化”的。
然而编程时不应该把重点放在优化上,应该关注扩展性。当今的WEB应用,需求变化非常之快,适应多种需求的架构是不存在的,我们的扩展性就要把要点放在跟底层交互的架构上,例如持久化数据的存取规则、缓存的存取规则等,还有一些共用服务,例如用户信息等。先把不变的部分做完善,剩下的部分就很容易将精力放在业务逻辑上面了。
posted @ 2012-02-17 12:27 attitudedecidesall Views(58) Comments(0) Edit 大型架构.net平台篇(中间层均衡负载WCF)第二部分 中间层均衡负载WCF
在第一部分的文章里,简单介绍了一下怎么在WEB层做均衡负载,主要用到的软件是Nginx.这里为啥引用中间层的概念呢?
最简单的部署方式: WEB层->访问DB, 这里直联数据库的做法,就是二层架构,WEB层和DB可以放在不同一个服务器上。在用户量和并发量大的时候,WEB层和DB压力都很大,而且还缺乏扩展性,所以大型架构都会采用三层的方式
三层架构部署方式:WEB层->中间层->DB层,WEB层不会直联数据库,WEB层,中间层,DB可以放在不同的服务器上。引用中间层的好处在于减轻了WEB层和DB压力,中间层专注于处理逻辑相关的业务,而且还提高网站的安全性,即使WEB层的服务器被攻破,也是没法获取到数据库的帐号和数据。三层架构的职能如下:
WEB层:只关注界面的展示,通过调中间层的结果获取数据来展示,不可以直接调用数据库取数据
中间层:只关注业务逻辑和调用数据库的数据
DB层:只部署数据库
在.net平台,中间层可以选择Webservice, WCF等等,考虑到安全性等原因,WCF在目前是非常好的选择。
WCF:如果不清楚可以网上搜索一下,使用起来和webservice是比较类似的,开发调试可能会麻烦点,熟悉了就觉得没什么麻烦的。
以下为.net平台下使用wcf作中间层的三层架构图
用WCF如何做均衡负载?
例如:中间层可以分三大块业务逻辑,订单服务(10001),商品服务(10002),用户服务(10003)。WCF部署成windows服务模式,即占用一个端口的windows进程
方法一:通过WEB层的分布式部署,中间层也跟着WEB层做相应的分布式部署,这个方法最简单,但不属于真正的均衡负载。
1.中间层部署
192.168.1.11 10001,10002,10003 这个服务器部署了三个WCF,端口号分别是10001,10002,10003
192.168.1.12 10001,10002,10003 这个服务器部署了三个WCF,端口号分别是10001,10002,10003
192.168.1.13 .....
.....
2.WEB层调用
WEB层服务器A:配置终结点为192.168.1.11的三个终结点
WEB层服务器B:配置终结点为192.168.1.12的三个终结点
WEB层服务器C:.....
.....
方法二:WEB层动态加载终结点,实现均衡负载的调用,本人未实践这种方法,理论上是可行的。
1.中间层部署
192.168.1.11 10001,10002,10003 这个服务器部署了三个WCF,端口号分别是10001,10002,10003
192.168.1.12 10001,10002,10003 这个服务器部署了三个WCF,端口号分别是10001,10002,10003
192.168.1.13 .....
2.各个WEB层读取同一份配置文件,配置的内容为各个服务的接口名和IP和端口,加载后生成数组
string []OrderService;
OrderService[0]="192.168.1.11:10001";
OrderService[1]="192.168.1.12:10001";
OrderService[2]="192.168.1.13:10001";
....
3.WEB层调用
通过随机算法,获取需要调用IP和端口
int index=new Radmon().Next(0,OrderService.Length);
string orderService=OrderService[index];
//最后调用相应IP和端口上的服务
posted @ 2012-02-17 12:20 attitudedecidesall Views(171) Comments(0) Edit SQL Server 2005实现负载均衡Internet的规模每一百天就会增长一倍,客户希望获得7天×24小时的不间断可用性及较快的系统反应时间,而不愿屡次看到某个站点“Server Too Busy”及频繁的系统故障。
随着业务量的提高,以及访问量和数据流量的快速增长,网络各个核心部分的处理性能和计算强度也相应增大,使得单一设备根本无法承担。在此情况下,如果扔掉现有设备去做大量的硬件升级,必将造成现有资源的浪费,而且下一次业务量的提升,又将导致再一次硬件升级的高额成本投入。于是,负载均衡机制应运而生。
对于负载均衡,笔者经常接触的当属Oracle的负载均衡机制。下面,我们先简单了解Oracle的负载均衡的实现方案。
Real Application Clusters是双机并行服务器(8i及以前版本称作Oracle Parallel Server,OPS),用来在集群环境下实现多机共享数据库,以保证应用的高可用性,同时可以自动实现并行处理及均分负载,还能实现数据库在故障时的排错和无断点恢复。它可以自动进行负载平衡、故障修复和规划停机时间,以支持高可用性应用程序。若并行服务器中某节点失效,透明的应用程序容错能够把用户自动转接到另一节点上继续运行,应用程序在用户没有察觉的情况下继续执行。这使周期性和非周期性发生故障的系统增大了连续可用性。进程的失效可以完全透明地转移到另一节点上去,通过适当地配置,可以指定所有查询都在客户端进行缓存,这样它们便可以在转移后的节点上重新设置。
下面我们重点介绍Sql Server 2005是如何实现负载均衡的。
Sql Server 2005的新特性
端到端拓扑的事务性复制
SQL Server 2005对端到端(P2P)拓扑结构上事务性的复制加强了支持。P2P的拓扑结构支持无限的发布服务器,它们彼此之间可以互相交换事务。
P2P拓扑是SQL Server的一个巨大进步。现在,多端点服务器可以更改数据,并且向其他的发布者复制事务。这就是说,订阅服务器不再被限制在主要的报告环境中,可以通过事务性负载全球共享的方式将服务器分布开来。当用户的数量增加的时候,只要简单地向这个群体中添加服务器即可。
除了将负载分布之外,这个拓扑结构还增加了可用性。如果任何一个点的服务器不可达,则池中其他服务器就会共享这个负载,因为每个服务器都有其他所有服务器上可获得的全部数据集合。
数据库镜像和快照
SQL Server 2005引入了数据库镜像的概念,来帮助获得高可用性。特别提醒的是,只要它正式发布了,数据库镜像就可以在SQL Server 2005上使用。然而,只有到SQL Server 2005 Service Pack 1才会支持镜像。
数据库快照是SQL Server 2005中引入的另一项特性。快照是某一个时间点上的数据库的克隆。只要对镜像数据库进行了快照,就可以让用户查询快照。快照的生成通常只需要几秒钟,因为它实际上在这个过程中并没有拷贝任何数据。因此,要把负载分布到主服务器和备用服务器上,就可以将数据库做镜像,然后阶段性地对备份服务器进行快照。而且还可以使用快照在主服务器上进行报告。
软件实现SQL Server 2005的负载均衡
中间层
实现数据库的负载均衡技术,首先要有一个可以控制连接数据库的控制端。在这里,它截断了数据库和程序的直接连接,由所有的程序来访问这个中间层,然后再由中间层来访问数据库。这样,我们就可以具体控制访问某个数据库了,然后还可以根据数据库的当前负载来调整每次连接到哪个数据库。好处在两个方面:首先,它成功地将数据库放到了内网之中,更好地保护了数据库的安全性。如果数据库也在公网上,1433端口是很容易被攻击的,所以要保护数据库与之的连接,就用到了中间层。它可以将数据库更加好地保护在内网。其次,连接数据库的所有连接都可以控制,更方便DBA对数据的管理,看哪些连接更耗费数据库资源,以便更好地优化代码。
但是,也有两点要注意:第一,必须要做成Windows的服务程序。Windows发展到今天,如果以一个集成的大系统来讲,做成服务程序更加稳定,也更加安全,这样做即使用户不登录机器,也可以使用。第二,必须要使用多个中间层。从中间层的作用可以看出,它承接了数据库的所有连接,所以,一旦出了问题,就会导致整个系统瘫痪。所以做多个中间层是必要的,这样,如果一个坏了可以登录到另一个。
实现多据库数据同步
中间层有了,下一步的工作是设置构建数据库集群。对于负载均衡,最重要的就是所有服务器的数据都是同步的。这是一个集群所必需的,因为,如果数据不同步,那么用户从一台服务器读出的数据,就有别于从另一台服务器读出的数据,这是不能允许的。所以必须实现一个数据库的数据同步。这里设置一个用于写入的数据库,设置两个用于读出的数据库,因为据统计,一般来讲,70%的数据库操作为读操作。
首先,在写入数据库上做一个发布服务器,主要基于SQL Server 2005的复制技术,将即将用到的表都选上。注意,在连接上要选用模拟用户,然后共享时选择sa用户,这样就可以将数据共享了。
其次,在两个读服务器上做订阅服务,要注意同样的事项,这样一个“一写两读”的数据库集群就完成了。
数据库的安全备份
在一个大的系统中,系统的无故障性是很重要的,但是在刚才的系统下,如果用于写的服务器突然坏了,整个系统就会出现问题,所以,再做个备份是必要的。
数据库镜像是SQL Server 2005大力推出的,它就是要实现数据库的安全转移,所以需要再单独拿出一台机器来做备份服务器,将完全镜像写入该服务器,这样,即使写入服务器坏了,它也可以自动转移到备份服务器上来,保证不影响用户。
这实际上就相当于实现了对服务器的容灾管理,但是有一点需要注意,在这种镜像的体系中,必须要有一台服务器作为监视服务器,以便察看哪台服务器坏了,用以在机器出错之后迅速调整。
回传数据库的状态
数据库服务器均已配完,整体的机器集群架构已经构建,接下来的工作就是配置程序。
首先,在读和写的服务器都放上一个监控程序,它同样必须是Windows的服务,这样更稳定;其次,它可以设定成每隔30秒或者一定时间,将服务器的CPU、内存、网卡流量和当前数据库状态等信息发送回来。在这里需要设置一个权值,用以衡量CPU、内存利用率等信息的各自比例。在这个系统中,建议以CPU利用率为准。
中间层实现的负载均衡
到此为止,所有的准备工作都已完成,包括数据库的建立和配置,中间层的位置,下面所作的就是用软件来实现这个负载均衡。
首先,当一个用户有数据库请求时,先判断是读还是写,如果是写的话,就直接返回写入服务器,这样当写服务器写完数据以后,差不多可以在3秒内返回其他两台机器。
其次,当遇到一个读的请求时,根据监控返回来的数据判断,根据刚才的权值返回一个当前最空闲的机器。需要注意的是,这时最好做一个记录器,用以保持一段时间的数值,可以让管理员自行设定,更好地做到几台数据库的压力平衡。
最后,如果为主的写入服务器突然坏掉,程序可以自动把备份的服务器切换过来,用刚才的备份服务器当作写服务器,然后做一个报警系统,用以通知管理员。同样,当监控服务器发现其他两台读服务器坏掉时,也会自动通知管理员,来处理服务器的异常情况,这样就可以保证系统的稳定运行,而且易于管理和维护。
总之,用软件和微软SQL Server 2005的一些新技术,可以很轻松地实现负载均衡,这样不但可以不用硬件来实现,方便管理员的操控,更有利于DBA管理数据库,及时发现问题。
posted @ 2012-02-17 12:17 attitudedecidesall Views(45) Comments(0) Edit 大型高性能ASP.NET系统架构设计大型动态应用系统平台主要是针对于大流量、高并发网站建立的底层系统架构。大型网站的运行需要一个可靠、安全、可扩展、易维护的应用系统平台做为支撑,以保证网站应用的平稳运行。
大型动态应用系统又可分为几个子系统:
Web前端系统、负载均衡系统、数据库集群系统、缓存系统、分布式存储系统、分布式服务器管理系统、代码分发系统
1、web前端系统
为了达到不同应用的服务器共享、避免单点故障、集中管理、统一配置等目的,不以应用划分服 务器,而是将所有服务器做统一使用,每台服务器都可以对多个应用提供服务,当某些应用访问量升高时,通过增加服务器节点达到整个服务器集群的性能提高,同时使他应用也会受益。
该Web前端系统基于IIS/ASP.NET等的虚拟主机平台,提供PHP程序运行环境。服务器对开发人员是透明的,不需要开发人员介入服务器管理。
2、负载均衡系统
负载均衡系统分为硬件和软件两种。硬件负载均衡效率高,但是价格贵,比如F5等。软件负载均衡系统价格较低或者免费,效率较硬件负载均衡系统低,不过对于流量一般或稍大些网站来讲也足够使用,比如lvs,nginx。大多数网站都是硬件、软件负载均衡系统并用。
3、数据库集群系统
由于Web前端采用了负载均衡集群结构提高了服务的有效性和扩展性,因此数据库必须也是高可靠的才能保证整个服务体系的高可靠性,如何构建一个高可靠的、可以提供大规模并发处理的数据库体系?
我们可以采用如上图所示的方案:
1)使用SQL数据库,考虑到Web应用的数据库读多写少的特点,我们主要对读数据库做了优化,提供专用的读数据库和写数据库,在应用程序中实现读操作和写操作分别访问不同的数据库。
2)使用同步机制实现快速将主库(写库)的数据库复制到从库(读库)。一个主库对应多个从库,主库数据实时同步到从库。
3)写数据库有多台,每台都可以提供多个应用共同使用,这样可以解决写库的性能瓶颈问题和单点故障问题。
4)读数据库有多台,通过负载均衡设备实现负载均衡,从而达到读数据库的高性能、高可靠和高可扩展性。
5)数据库服务器和应用服务器分离。
6)从数据库使用BigIP做负载均衡。
4、缓存系统
缓存分为文件缓存、内存缓存、数据库缓存。在大型Web应用中使用最多且效率最高的是内存缓存。最常用的内存缓存工具是Memcachd。使用正确的缓存系统可以达到实现以下目标:
1、使用缓存系统可以提高访问效率,提高服务器吞吐能力,改善用户体验。
2、减轻对数据库及存储集服务器的访问压力。
3、Memcached服务器有多台,避免单点故障,提供高可靠性和可扩展性,提高性能。
5、分布式存储系统
Web系统平台中的存储需求有下面两个特点:
1) 存储量很大,经常会达到单台服务器无法提供的规模,比如相册、视频等应用。因此需要专业的大规模存储系统。
2) 负载均衡cluster中的每个节点都有可能访问任何一个数据对象,每个节点对数据的处理也能被其他节点共享,因此这些节点要操作的数据从逻辑上看只能是一个整体,不是各自独立的数据资源。
因此高性能的分布式存储系统对于大型网站应用来说是非常重要的一环。(这个地方需要加入对某个分布式存储系统的简单介绍。)
6、分布式服务器管理系统
随着网站访问流量的不断增加,大多的网络服务都是以负载均衡集群的方式对外提供服务,随之集群规模的扩大,原来基于单机的服务器管理模式已经不能够满足我们的需求,新的需求必须能够集中式的、分组的、批量的、自动化的对服务器进行管理,能够批量化的执行计划任务。
在分布式服务器管理系统软件中有一些比较优秀的软件,其中比较理想的一个是Cfengine。它可以对服务器进行分组,不同的分组可以分别定制系统配置文件、计划任务等配置。
它是基于C/S 结构的,所有的服务器配置和管理脚本程序都保存在Cfengine Server上,而被管理的服务器运行着 Cfengine Client程序,Cfengine Client通过SSL加密的连接定期的向服务器端发送请求以获取最新的配置文件和管理命令、脚本程序、补丁安装等任务。
有了Cfengine 这种集中式的服务器管理工具,我们就可以高效的实现大规模的服务器集群管理,被管理服务器和 Cfengine Server可以分布在任何位置,只要网络可以连通就能实现快速自动化的管理。
7、代码分发系统
随着网站访问流量的不断增加,大多的网络服务都是以负载均衡集群的方式对外提供服务,随之集群规模的扩大,为了满足集群环境下程序代码的批量分发和更新,我们还需要一个程序代码发布系统。
这个发布系统可以帮我们实现下面的目标:
1) 生产环境的服务器以虚拟主机方式提供服务,不需要开发人员介入维护和直接操作,提供发布系统可以实现不需要登陆服务器就能把程序分发到目标服务器。
2) 我们要实现内部开发、内部测试、生产环境测试、生产环境发布的4个开发阶段的管理,发布系统可以介入各个阶段的代码发布。
3) 我们需要实现源代码管理和版本控制,SVN可以实现该需求。
这里面可以使用常用的工具Rsync,通过开发相应的脚本工具实现服务器集群间代码同步分发。