网站优化技术

网站优化技术

1大型网站在架构上应当考虑哪些问题?

答:

– 分层:分层是处理任何复杂系统最常见的手段之一,将系统横向切分成若干

个层面,每个层面只承担单一的职责,然后通过下层为上层提供的基础设施和

服务以及上层对下层的调用来形成一个完整的复杂的系统。计算机网络的开放

系统互联参考模型(OSI/RM)和Internet 的 TCP/IP模型都是分层结构,大型

网站的软件系统也可以使用分层的理念将其分为持久层(提供数据存储和访问

服务)、业务层(处理业务逻辑,系统中最核心的部分)和表示层(系统交

互、视图展示)。需要指出的是:(1)分层是逻辑上的划分,在物理上可以位

于同一设备上也可以在不同的设备上部署不同的功能模块,这样可以使用更多

的计算资源来应对用户的并发访问;(2)层与层之间应当有清晰的边界,这样

分层才有意义,才更利于软件的开发和维护。

– 分割:分割是对软件的纵向切分。我们可以将大型网站的不同功能和服务分

割开,形成高内聚低耦合的功能模块(单元)。在设计初期可以做一个粗粒度

的分割,将网站分割为若干个功能模块,后期还可以进一步对每个模块进行细

粒度的分割,这样一方面有助于软件的开发和维护,另一方面有助于分布式的

部署,提供网站的并发处理能力和功能的扩展。

– 分布式:除了上面提到的内容,网站的静态资源(JavaScript、CSS、图片

等)也可以采用独立分布式部署并采用独立的域名,这样可以减轻应用服务器

的负载压力,也使得浏览器对资源的加载更快。数据的存取也应该是分布式

的,传统的商业级关系型数据库产品基本上都支持分布式部署,而新生的

NoSQL产品几乎都是分布式的。当然,网站后台的业务处理也要使用分布式技

术,例如查询索引的构建、数据分析等,这些业务计算规模庞大,可以使用

Hadoop以及 MapReduce 分布式计算框架来处理。

– 集群:集群使得有更多的服务器提供相同的服务,可以更好的提供对并发的

支持。

– 缓存:所谓缓存就是用空间换取时间的技术,将数据尽可能放在距离计算最

近的位置。使用缓存是网站优化的第一定律。我们通常说的 CDN、反向代理、

热点数据都是对缓存技术的使用。

– 异步:异步是实现软件实体之间解耦合的又一重要手段。异步架构是典型的

生产者消费者模式,二者之间没有直接的调用关系,只要保持数据结构不变,

彼此功能实现可以随意变化而不互相影响,这对网站的扩展非常有利。使用异

步处理还可以提高系统可用性,加快网站的响应速度(用 Ajax加载数据就是一

种异步技术),同时还可以起到削峰作用(应对瞬时高并发)。"能推迟

处理的都要推迟处理”是网站优化的第二定律,而异步是践行网站优化第二定律

的重要手段。

– 冗余:各种服务器都要提供相应的冗余服务器以便在某台或某些服务器宕机

时还能保证网站可以正常工作,同时也提供了灾难恢复的可能性。冗余是网站

高可用性的重要保证。

2、你用过的网站前端优化的技术有哪些?

答:

① 浏览器访问优化:

– 减少 HTTP 请求数量:合并 CSS、合并 JavaScript、合并图片(CSS Sprite)

– 使用浏览器缓存:通过设置 HTTP响应头中的 Cache-Control和 Expires 属

性,将CSS、JavaScript、图片等在浏览器中缓存,当这些静态资源需要更新

时,可以更新HTML 文件中的引用来让浏览器重新请求新的资源

– 启用压缩

– CSS 前置,JavaScript 后置

– 减少 Cookie 传输

② CDN加速:CDN(Content Distribute Network)的本质仍然是缓存,将数

据缓存在离用户最近的地方,CDN通常部署在网络运营商的机房,不仅可以提

升响应速度,还可以减少应用服务器的压力。当然,CDN缓存的通常都是静态

资源。

③ 反向代理:反向代理相当于应用服务器的一个门面,可以保护网站的安全

性,也可以实现负载均衡的功能,当然最重要的是它缓存了用户访问的热点资

源,可以直接从反向代理将某些内容返回给用户浏览器。

3、你使用过的应用服务器优化技术有哪些?

答:

① 分布式缓存:缓存的本质就是内存中的哈希表,如果设计一个优质的哈希函

数,那么理论上哈希表读写的渐近时间复杂度为 O(1)。缓存主要用来存放那些

读写比很高、变化很少的数据,这样应用程序读取数据时先到缓存中读取,如

果没有或者数据已经失效再去访问数据库或文件系统,并根据拟定的规则将数

据写入缓存。对网站数据的访问也符合二八定律(Pareto分布,幂律分布),

即80%的访问都集中在 20%的数据上,如果能够将这 20%的数据缓存起来,那么

系统的性能将得到显著的改善。当然,使用缓存需要解决以下几个问题:

– 频繁修改的数据;

– 数据不一致与脏读;

– 缓存雪崩(可以采用分布式缓存服务器集群加以解决,memcached 是广泛采

用的解决方案);

– 缓存预热;

– 缓存穿透(恶意持续请求不存在的数据)。

② 异步操作:可以使用消息队列将调用异步化,通过异步处理将短时间高并发

产生的事件消息存储在消息队列中,从而起到削峰作用。电商网站在进行促销

活动时,可以将用户的订单请求存入消息队列,这样可以抵御大量的并发订单

请求对系统和数据库的冲击。目前,绝大多数的电商网站即便不进行促销活

动,订单系统都采用了消息队列来处理。

③ 使用集群。

④ 代码优化:

– 多线程:基于 Java 的 Web 开发基本上都通过多线程的方式响应用户的并发请

求,使用多线程技术在编程上要解决线程安全问题,主要可以考虑以下几个方

面:A. 将对象设计为无状态对象(这和面向对象的编程观点是矛盾的,在面向

对象的世界中被视为不良设计),这样就不会存在并发访问时对象状态不一致

的问题。B. 在方法内部创建对象,这样对象由进入方法的线程创建,不会出现

多个线程访问同一对象的问题。使用 ThreadLocal 将对象与线程绑定也是很好

的做法,这一点在前面已经探讨过了。C. 对资源进行并发访问时应当使用合理

的锁机制。

– 非阻塞 I/O: 使用单线程和非阻塞 I/O 是目前公认的比多线程的方式更能充

分发挥服务器性能的应用模式,基于 Node.js 构建的服务器就采用了这样的方

式。Java 在 JDK 1.4 中就引入了NIO(Non-blocking I/O),在Servlet 3 规范

中又引入了异步Servlet 的概念,这些都为在服务器端采用非阻塞 I/O 提供了

必要的基础。

– 资源复用:资源复用主要有两种方式,一是单例,二是对象池,我们使用的

数据库连接池、线程池都是对象池化技术,这是典型的用空间换取时间的策

略,另一方面也实现对资源的复用,从而避免了不必要的创建和释放资源所带

来的开销。

4、什么是 XSS 攻击?什么是 SQL 注入攻击?什么是 CSRF 攻击?

答:

– XSS(Cross Site Script,跨站脚本攻击)是向网页中注入恶意脚本在用户

浏览网页时在用户浏览器中执行恶意脚本的攻击方式。跨站脚本攻击分有两种

形式:反射型攻击(诱使用户点击一个嵌入恶意脚本的链接以达到攻击的目

标,目前有很多攻击者利用论坛、微博发布含有恶意脚本的 URL就属于这种方

式)和持久型攻击(将恶意脚本提交到被攻击网站的数据库中,用户浏览网页

时,恶意脚本从数据库中被加载到页面执行,QQ 邮箱的早期版本就曾经被利用

作为持久型跨站脚本攻击的平台)。XSS 虽然不是什么新鲜玩意,但是攻击的

手法却不断翻新,防范 XSS 主要有两方面:消毒(对危险字符进行转义)和

HttpOnly(防范 XSS 攻击者窃取Cookie 数据)。

– SQL 注入攻击是注入攻击最常见的形式(此外还有 OS注入攻击(Struts 2 的

高危漏洞就是通过 OGNL 实施 OS注入攻击导致的)),当服务器使用请求参数

构造SQL 语句时,恶意的 SQL 被嵌入到SQL 中交给数据库执行。SQL 注入攻击

需要攻击者对数据库结构有所了解才能进行,攻击者想要获得表结构有多种方

式:(1)如果使用开源系统搭建网站,数据库结构也是公开的(目前有很多现

成的系统可以直接搭建论坛,电商网站,虽然方便快捷但是风险是必须要认真

评估的);(2)错误回显(如果将服务器的错误信息直接显示在页面上,攻击

者可以通过非法参数引发页面错误从而通过错误信息了解数据库结构,Web 应

用应当设置友好的错误页,一方面符合最小惊讶原则,一方面屏蔽掉可能给系

统带来危险的错误回显信息);(3)盲注。防范 SQL注入攻击也可以采用消毒

的方式,通过正则表达式对请求参数进行验证,此外,参数绑定也是很好的手

段,这样恶意的SQL 会被当做 SQL的参数而不是命令被执行,JDBC 中的

PreparedStatement 就是支持参数绑定的语句对象,从性能和安全性上都明显

优于Statement。

– CSRF 攻击(Cross Site Request Forgery,跨站请求伪造)是攻击者通过跨

站请求,以合法的用户身份进行非法操作(如转账或发帖等)。CSRF 的原理是

利用浏览器的Cookie 或服务器的Session,盗取用户身份,其原理如下图所

示。防范CSRF 的主要手段是识别请求者的身份,主要有以下几种方式:(1)

在表单中添加令牌(token);(2)验证码;(3)检查请求头中的 Referer

(前面提到防图片盗链接也是用的这种方式)。令牌和验证都具有一次消费性

的特征,因此在原理上一致的,但是验证码是一种糟糕的用户体验,不是必要

的情况下不要轻易使用验证码,目前很多网站的做法是如果在短时间内多次提

交一个表单未获得成功后才要求提供验证码,这样会获得较好的用户体验。

补充:防火墙的架设是 Web 安全的重要保障,ModSecurity是开源的 Web 防火

墙中的佼佼者。企业级防火墙的架设应当有两级防火墙,Web服务器和部分应

用服务器可以架设在两级防火墙之间的 DMZ,而数据和资源服务器应当架设在

第二级防火墙之后。

5. 什么是领域模型(domain model)?贫血模型(anaemic domain model)和

充血模型(rich domain model)有什么区别?

答:领域模型是领域内的概念类或现实世界中对象的可视化表示,又称为概念

模型或分析对象模型,它专注于分析问题领域本身,发掘重要的业务领域概

念,并建立业务领域概念之间的关系。贫血模型是指使用的领域对象中只有

setter和 getter 方法(POJO),所有的业务逻辑都不包含在领域对象中而是

放在业务逻辑层。有人将我们这里说的贫血模型进一步划分成失血模型(领域

对象完全没有业务逻辑)和贫血模型(领域对象有少量的业务逻辑),我们这

里就不对此加以区分了。充血模型将大多数业务逻辑和持久化放在领域对象

中,业务逻辑(业务门面)只是完成对业务逻辑的封装、事务和权限等的处

理。下面两张图分别展示了贫血模型和充血模型的分层架构。

贫血模型

充血模型

贫血模型下组织领域逻辑通常使用事务脚本模式,让每个过程对应用户可能要

做的一个动作,每个动作由一个过程来驱动。也就是说在设计业务逻辑接口的

时候,每个方法对应着用户的一个操作,这种模式有以下几个有点:

– 它是一个大多数开发者都能够理解的简单过程模型(适合国内的绝大多数开

发者)。

– 它能够与一个使用行数据入口或表数据入口的简单数据访问层很好的协作。

– 事务边界的显而易见,一个事务开始于脚本的开始,终止于脚本的结束,很

容易通过代理(或切面)实现声明式事务。

然而,事务脚本模式的缺点也是很多的,随着领域逻辑复杂性的增加,系统的

复杂性将迅速增加,程序结构将变得极度混乱。

6. 谈一谈测试驱动开发(TDD)的好处以及你的理解。

答:TDD 是指在编写真正的功能实现代码之前先写测试代码,然后根据需要重

构实现代码。在JUnit 的作者 Kent Beck的大作《测试驱动开发:实战与模式

解析》(Test-Driven Development: by Example)一书中有这么一段内容:

“消除恐惧和不确定性是编写测试驱动代码的重要原因”。因为编写代码时的

恐惧会让你小心试探,让你回避沟通,让你羞于得到反馈,让你变得焦躁不

安,而TDD 是消除恐惧、让 Java开发者更加自信更加乐于沟通的重要手段。

TDD会带来的好处可能不会马上呈现,但是你在某个时候一定会发现,这些好

处包括:

– 更清晰的代码 — 只写需要的代码

– 更好的设计

– 更出色的灵活性 — 鼓励程序员面向接口编程

– 更快速的反馈 — 不会到系统上线时才知道 bug 的存在

补充:敏捷软件开发的概念已经有很多年了,而且也部分的改变了软件开发这

个行业,TDD 也是敏捷开发所倡导的。

TDD可以在多个层级上应用,包括单元测试(测试一个类中的代码)、集成测

试(测试类之间的交互)、系统测试(测试运行的系统)和系统集成测试(测

试运行的系统包括使用的第三方组件)。TDD 的实施步骤是:红(失败测试)-

绿(通过测试) – 重构。关于实施TDD 的详细步骤请参考另一篇文章《测试驱

动开发之初窥门径》。

在使用TDD 开发时,经常会遇到需要被测对象需要依赖其他子系统的情况,但

是你希望将测试代码跟依赖项隔离,以保证测试代码仅仅针对当前被测对象或

方法展开,这时候你需要的是测试替身。测试替身可以分为四类:

– 虚设替身:只传递但是不会使用到的对象,一般用于填充方法的参数列表

– 存根替身:总是返回相同的预设响应,其中可能包括一些虚设状态

– 伪装替身:可以取代真实版本的可用版本(比真实版本还是会差很多)

– 模拟替身:可以表示一系列期望值的对象,并且可以提供预设响应

Java世界中实现模拟替身的第三方工具非常多,包括 EasyMock、Mockito、

jMock等。

点赞