Posted: November 15, 2006 at 3:54 pm | Tags: cache, class, debug, html, java, linux, php, python, ruby, server, web, 优化, 测试, 类, 缓存
一、memcached 简介
在很多场合,我们都会听到 memcached 这个名字,但很多同学只是听过,并没有用过或实际了解过,只知道它是一个很不错的东东。这里简单介绍一下,memcached 是高效、快速的分布式内存对象缓存系统,主要用于加速 WEB 动态应用程序。
二、memcached 安装
首先是下载 memcached 了,目前最新版本是 1.1.12,直接从官方网站即可下载到 memcached-1.1.12.tar.gz。除此之外,memcached 用到了 libevent,我下载的是 libevent-1.1a.tar.gz。
接下来是分别将 libevent-1.1a.tar.gz 和 memcached-1.1.12.tar.gz 解开包、编译、安装:
# tar -xzf libevent-1.1a.tar.gz# cd libevent-1.1a# ./configure --prefix=/usr# make# make install# cd ..# tar -xzf memcached-1.1.12.tar.gz# cd memcached-1.1.12# ./configure --prefix=/usr# make# make install
安装完成之后,memcached 应该在 /usr/bin/memcached。
三、运行 memcached 守护程序
运行 memcached 守护程序很简单,只需一个命令行即可,不需要修改任何配置文件(也没有配置文件给你修改
):
/usr/bin/memcached -d -m 128 -l 192.168.1.1 -p 11211 -u httpd
参数解释:
-d 以守护程序(daemon)方式运行 memcached;-m 设置 memcached 可以使用的内存大小,单位为 M;-l 设置监听的 IP 地址,如果是本机的话,通常可以不设置此参数;-p 设置监听的端口,默认为 11211,所以也可以不设置此参数;-u 指定用户,如果当前为 root 的话,需要使用此参数指定用户。
当然,还有其它参数可以用,man memcached 一下就可以看到了。
四、memcached 的工作原理
首先 memcached 是以守护程序方式运行于一个或多个服务器中,随时接受客户端的连接操作,客户端可以由各种语言编写,目前已知的客户端 API 包括 Perl/PHP/Python/Ruby/Java/C#/C 等等。PHP 等客户端在与 memcached 服务建立连接之后,接下来的事情就是存取对象了,每个被存取的对象都有一个唯一的标识符 key,存取操作均通过这个 key 进行,保存到 memcached 中的对象实际上是放置内存中的,并不是保存在 cache 文件中的,这也是为什么 memcached 能够如此高效快速的原因。注意,这些对象并不是持久的,服务停止之后,里边的数据就会丢失。
三、PHP 如何作为 memcached 客户端
有两种方法可以使 PHP 作为 memcached 客户端,调用 memcached 的服务进行对象存取操作。
第一种,PHP 有一个叫做 memcache 的扩展,Linux 下编译时需要带上 –enable-memcache[=DIR] 选项,Window 下则在 php.ini 中去掉 php_memcache.dll 前边的注释符,使其可用。
除此之外,还有一种方法,可以避开扩展、重新编译所带来的麻烦,那就是直接使用 php-memcached-client。
本文选用第二种方式,虽然效率会比扩展库稍差一些,但问题不大。
四、PHP memcached 应用示例
首先 下载 memcached-client.php,在下载了 memcached-client.php 之后,就可以通过这个文件中的类“memcached”对 memcached 服务进行操作了。其实代码调用非常简单,主要会用到的方法有 add()、get()、replace() 和 delete(),方法说明如下:
add ($key, $val, $exp = 0)
往 memcached 中写入对象,$key 是对象的唯一标识符,$val 是写入的对象数据,$exp 为过期时间,单位为秒,默认为不限时间;
get ($key)
从 memcached 中获取对象数据,通过对象的唯一标识符 $key 获取;
replace ($key, $value, $exp=0)
使用 $value 替换 memcached 中标识符为 $key 的对象内容,参数与 add() 方法一样,只有 $key 对象存在的情况下才会起作用;
delete ($key, $time = 0)
删除 memcached 中标识符为 $key 的对象,$time 为可选参数,表示删除之前需要等待多长时间。
下面是一段简单的测试代码,代码中对标识符为 ‘mykey’ 的对象数据进行存取操作:
<pre><?php// 包含 memcached 类文件require_once(‘memcached-client.php’);// 选项设置$options = array( ’servers’ => array(‘192.168.1.1:11211′), //memcached 服务的地址、端口,可用多个数组元素表示多个 memcached 服务 ‘debug’ => true, //是否打开 debug ‘compress_threshold’ => 10240, //超过多少字节的数据时进行压缩 ‘persistant’ => false //是否使用持久连接 );// 创建 memcached 对象实例$mc = new memcached($options);// 设置此脚本使用的唯一标识符$key = ‘mykey’;// 往 memcached 中写入对象$mc->add($key, ’some random strings’);$val = $mc->get($key);echo “n”.str_pad(‘$mc->add() ’, 60, ‘_’).“n”;var_dump($val);// 替换已写入的对象数据值$mc->replace($key, array(’some’=>‘haha’, ‘array’=>‘xxx’));$val = $mc->get($key);echo “n”.str_pad(‘$mc->replace() ’, 60, ‘_’).“n”;var_dump($val);// 删除 memcached 中的对象$mc->delete($key);$val = $mc->get($key);echo “n”.str_pad(‘$mc->delete() ’, 60, ‘_’).“n”;var_dump($val);?></pre>
是不是很简单,在实际应用中,通常会把数据库查询的结果集保存到 memcached 中,下次访问时直接从 memcached 中获取,而不再做数据库查询操作,这样可以在很大程度上减轻数据库的负担。通常会将 SQL 语句 md5() 之后的值作为唯一标识符 key。下边是一个利用 memcached 来缓存数据库查询结果集的示例(此代码片段紧接上边的示例代码):
<?php$sql = ‘SELECT * FROM users’;$key = md5($sql); //memcached 对象标识符if ( !($datas = $mc->get($key)) ) { // 在 memcached 中未获取到缓存数据,则使用数据库查询获取记录集。 echo “n”.str_pad(‘Read datas from MySQL.’, 60, ‘_’).“n”; $conn = mysql_connect(‘localhost’, ‘test’, ‘test’); mysql_select_db(‘test’); $result = mysql_query($sql); while ($row = mysql_fetch_object($result)) $datas[] = $row; // 将数据库中获取到的结果集数据保存到 memcached 中,以供下次访问时使用。 $mc->add($key, $datas);} else { echo “n”.str_pad(‘Read datas from memcached.’, 60, ‘_’).“n”;}var_dump($datas);?>
可以看出,使用 memcached 之后,可以减少数据库连接、查询操作,数据库负载下来了,脚本的运行速度也提高了。
之前我曾经写过一篇名为《PHP 实现多服务器共享 SESSION 数据》文章,文中的 SESSION 是使用数据库保存的,在并发访问量大的时候,服务器的负载会很大,经常会超出 MySQL 最大连接数,利用 memcached,我们可以很好地解决这个问题,工作原理如下:
- 用户访问网页时,查看 memcached 中是否有当前用户的 SESSION 数据,使用 session_id() 作为唯一标识符;如果数据存在,则直接返回,如果不存在,再进行数据库连接,获取 SESSION 数据,并将此数据保存到 memcached 中,供下次使用;
- 当前的 PHP 运行结束(或使用了 session_write_close())时,会调用 My_Sess::write() 方法,将数据写入数据库,这样的话,每次仍然会有数据库操作,对于这个方法,也需要进行优化。使用一个全局变量,记录用户进入页面时的 SESSION 数据,然后在 write() 方法内比较此数据与想要写入的 SESSION 数据是否相同,不同才进行数据库连接、写入数据库,同时将 memcached 中对应的对象删除,如果相同的话,则表示 SESSION 数据未改变,那么就可以不做任何操作,直接返回了;
- 那么用户 SESSION 过期时间怎么解决呢?记得 memcached 的 add() 方法有个过期时间参数 $exp 吗?把这个参数值设置成小于 SESSION 最大存活时间即可。另外别忘了给那些一直在线的用户延续 SESSION 时长,这个可以在 write() 方法中解决,通过判断时间,符合条件则更新数据库数据。
五、相关资源
肖理达 (KrazyNio AT hotmail.com), 2006.04. 06, 转载请注明出处
转载自:http://nio.infor96.com/php-memcached/
Posted: November 5, 2006 at 6:57 pm | Tags: class, html, 优化, 开发, 技术, 类, 缓存
Andrei Alexandrescu
想象本篇你正要读的“泛型<编程>”部分的开头是:“本文关于怎样用C++处理内存缓冲”。
当你轻率地关掉浏览器时,如果竖起耳朵,你还会听到成千上万的鼠标在做和你一样的事情。因为谁会对处理内存缓冲这样的小事感兴趣呢?
但本文确实关于怎样在C++中处理内存缓冲,但这里有两个特殊之处。首先,缓冲是泛型的,这意味着缓冲里面能放置类型化数据(相反于原始字节)。第二,我们要讨论的缓存的效率和存放在里面的类型及所在操作系统容许的最高效率相当,这种效率是指在任何方面的效率——包括分配策略,和数据操作。
你会很快看到,写一个泛型缓存是个关于模板的小小练习。写一个高效的的缓存也不是一个复杂的任务。写一个意外安全的缓存难一些,但还不是很难,特别在你读过关于意外安全的书[1]之后。但写一个同时具备泛型特征和高效率的缓存就象爬一个险峰。就象在C++中常常发生的,你在山顶欣赏到的景色使你的努力获得足够回报——只要你能克服路上遇到困难。
真空地带
有些人总会时不时在Usenet新闻组comp.lang.c++.moderated中张贴这样的问题:为什么autp_ptr不能给你在析构函数中使用delete[]而不是delete的机会?接下去的讨论通常会这样:
“你不应该使用C风格的数组而应该用std::vector,它高效又强大”
“但std::vector在我要用到的地方不够高效。”
“为什么?”等等
事实是,std::vector确实是个用来处理连续对象序列的设计精良的类。当我首次看到std::vector的优良设计时,我根本没有把它扔掉而自己来写一个的需要(当我看到,比如说,某些MFC的东东时我感觉到了这种冲动)然而。std::vector是有些不够高效之处,而这可能会严重影响到某些使用者。
* 不必要的数据初始化。通常情况,你需要创建一个大小合适的vector,带有基本类型(比如char)并把它传递给低层C函数来用一个socket或文件中读取的数据来填充它。问题在于,尽管你不需要,整个vector都会通过vector的构造函数或resize函数初始化。如果你必须和C函数打夹道,你就没办法避免这个开销。
* 指数增长。标准vector规则要求快速的(平均为常量时间)的增长策略。这就要求std::vector以乘法来增长——大多数vector实现当需要自动增长时分配倍增的内存。比如如果你要在一个包含一百万对象的vector后面添加一个对象,这个vector为了将来短时间内不再增长会分配能容纳三百万对象的空间,但这可能不是你想要的。
* “强制性”内存分配策略。标准vector永远不会收缩而只会增长。如果你集中了三百万数据样本,然后你觉得做插值计算的话只需要保留一百万数据样本,你就会有能存放二百万对象的空闲空间。处理这种情况建议使用的常用手段是把你的vector拷到一个新建的vector中然后互换这两个vector
std::vector<double> data;
…处理数据…
//收缩数据
std::vector<double>(data.begin(), data.end()).swap(data);
这个常用法不是很优化,因为你必须拷贝里面的数据。更糟的是,就是这样一个常用法也不能保证对内存百分之百的利用率。因为std::vector会总是分配比需要多的内存。如果你能够“就地”收缩vector会更好,也就是说,这需要告诉内存分配器它可以使用你的vector尾端之后的内存。许多内存分配器允许你这样做。
* 低效的对象移动。std::vector对拷贝和移动做无差别处理。对一个std::vector来说,一个移动操作就是一个拷贝操作后面跟一个对源对象的析构操作。所以如果你有一个保存字符串的vector并且为它重新分配,你没办法写一个函数来快速地移动几个指针(译注:此处指针指的是比如指向所管理内存的开始和结尾等这些地方的指针)到新的内存来使std::vector使用它。每个单个的字符串都毫无必要地拷贝到一个新的字符串。当被移动对象不是CRC[2]成员,那这将会是个低效率的巨大来源。这也是常常说到的怎样的std::vector<std::vector<T> >会被禁止的原因。
* 数据拷贝和移动中更多的低效之处。一些STL的实现不能区分包含旧数据类型的vector和包含用户自定类型的vector。(Metrowerks是个例外)这意味着它们使用更通用的结构来拷贝数据,比如for循环。你有权认为一个好的编译器应该把拷贝一系列整数的loop替换为更高效的memcpy调用。传说中两个能产生高效代码的编译器,Microsoft Visual C++和Metrowerks CodeWarrior,在完全速度优化方式下,使用loop来拷贝整型数据产生的代码比对应的memcpy调用明显要慢。所以你要执行效率,你需要在你自己的代码中调用memcpy。
* 不必要的意外安全。许多std::vector实现(但不是全部)假设所存类型的构造函数,拷贝构造函数,和赋值操作符重载函数可能抛出意外。但这对基本类型和其他许多类型都不会发生。所以这些实现对这些类型产生的代码可能会不必要的更大和更慢。
在许多应用中,这些问题的部分或全部根本没什么影响。但当你有苛刻的性能要求时
std::vector就不那么吸引人了。你会转而寻找一个更优化也给你更多控制权的结构。问题是你无法在C++标准中的std::vector下层找到任何容器——除了C风格数组。你要么开卡迪拉克,要么骑黄鱼车。
要证据?证据就在你面前:STL在至少三处使用连续内存缓冲(除了std::vector本身以外):std::basic_string,std::deque的内存块管理部分和std::deque的内存块本身。然而。我没看到有STL实现使用vector作为这些结构的后端。如果说std::vector是你应用中实现连续内存唯一能用的工具,那就是说STL实现有其他特别工具来作为它们的后端。
有一个空隙,在std::vector和C风格数组中有一个真空地带。我们定位于这个位置。我们会开发一个包含下列特性的连续内存缓冲:
* 泛型化的—可以存放任意类型序列
* 亲和的—支持std::vector语法和语义的子集
* 提供完全控制——提供细粒度的基本操作加上高层次的函数
* 产生的代码对基本类型和选定的用户定义类型尽最大可能优化
* 允许用户控制的优化
* 支持高级内存分配功能,例如就地扩展和快速重分配
* 最最重要的,能够被用做更高层连续内存容器的后端——特别是,你能用它实现vector,string或者deque.
buffer:第一线曙光
我们来为buffer模类定义一个接口。buffer只保存两个指针——所控制内存块的头和尾。对一个buffer来说,尺寸和容积(size and capacity)没有区别。我们就从std::vector开始,下手去除和容积有关的函数,我们剩下下列成员函数集:
template <class T, class Allocater = std::allocator<T> >
class buffer
{
public:
….所有std::vector的类型定义和函数,除了:
//size_type capacity() const;
//void reserve(size_type n);
private:
T* beg_;
T* end_;
};
有趣的是,尽管buffer没有容积概念,但它能够实现std::vector的所有功能,除了capacity和reserve,还有buffer不能满足所有这些函数的性能要求。比如说,buffer<T>::push_back有O(N)的时间复杂度,但std::vector<T>::push_back综合于大量的调用时有O(1)的时间复杂度。(这是标准所说的“摊分常量时间(amortized constant)”)你看到后面就知道能够在某些情况下提高buffer<T>::push_back的性能而仍旧不必支持容积概念。
实现buffer的接口并不很复杂,两个地方需要注意:意外安全和正确地摧毁对象。标准函数std::uninitialized_copy和std::uninitialized_fill是两个很有用的工具。
为了允许用户分配一块缓存而不初始化它,我们需要一个特别的构造函数和几个辅助函数。然后,我们需要一个grow_noinit工具来扩展缓存而不调用构造函数。对应的,我们需要shrink_nodestroy函数来收缩缓存而不调用析构函数,最后,需要一个稍微有些多余的函数clear_nodestroy,它清空缓存,回收内存而不调用析构函数。
template <class T, class Allocator = std::allocator<T> >
class buffer
{
….同上….
public:
enum noinit_t { noinit };
buffer(size_type n, noinit_t,
const Allocator& a = Allocator());
void grow_noinit (size_type growBy);
void shrink_nodestroy(size_type shrinkBy);
void clear_nodestroy();
};
这个扩展的接口给了你对缓存储存在内数据的完全控制。但是注意不要不假思索地直接使用这些扩展函数。比如说你象下面那样使用grow_noinit:
void Fun()
{
buffer<Widget> someWidgets;
….
//加10个Widget的空间
someWidgets.grow_noinit(10);
//初始化这些Widget
ConstructWidgets(
SomeWidgets.end() – 10, someWidgets.end());
….
};
这里的问题在于如果10个Widget的构造函数如果以任意形式失败,所有事情都会是一团糟。当Fun返回,someWidgets的析构函数会摧毁它里面包含对象——它不会知道哪些Widget构造成功哪些没有,这也是因为buffer不象std::vector有容积的概念,如果还有buffer内还有没初始化的内存,对那些内存使用Widget的析构函数很明显会导致未定义后果。
类型特性(Type Traits)
一项优化泛型代码的关键技术是获取泛型代码操作的类型的信息。这样,泛型代码能够在编译时分配工作给指定的代码,这些代码都对专门类型执行操作。
比如在我们的例子中,一个很重要的信息是buffer内的类型的拷贝构造函数是否抛出意外。对一个拷贝时不抛意外的类型,代码就会因不用处理意外安全而变得简单。此外,一些编译器如没有用try块会产生更好的代码。
类型特性是一个用来推导类型信息的有名技术。Boost[4]有个库实现类型特性,Loki[5]也有(在buffer中,我们会用到的类型特性机制会和Boost和Loki的都稍有不同,这是由Kavonen通过电邮建议我的)。
我们来看怎样推导一个类型的拷贝函数是否抛意外。坦白地说,C++中不可能知道所有类型的拷贝函数是否抛意外。但是,我们做一些工作并让用户当他们觉得需要优化时来帮我们做到。
你不必成为歇洛克?福尔摩斯就能够知道任何一个基本类型的拷贝构造函数不会抛出意外。这样,一个保守的假设是任何除了基本类型的类型的拷贝构造函数可能抛出意外。下面是对应代码:
namespace TypeTraits
{
//列举所有基本类型
typedef TYPELIST_14(
const bool,
const char,
const signed char,
const unsigned char,
const wchar_t,
const short int,
const unsigned short int,
const int,
const unsigned int,
const long int,
const unsigned long int,
const float,
const double,
const long double)
PrimitiveTypes;
template <typename T> struct IsPrimitive
{
enum { value =
Loki::TL::IndexOf<PrimitiveTypes,
Const T<::value >= 0};
};
template <typename T> struct IsPrimitive<T*>
{
enum {value = true };
};
template <typename T> struct CopyThrows
{
enum {value = !IsPrimitive<T>::value };
};
}
为了简明,上面的代码使用了Loki提供的两个工具:typelists和indexOf。typellists让你创建和操作类型串,Loki::TL::IndexOf在类型串中查找一个单独类型并且返回它在其中的索引。如果类型串中没有找到这个类型,返回的索引为负。最终,TypeTraits::CopyThrow<T>::value包含了所需信息。
通过这个机制来获取类型信息非常非常灵活,假设你在一个应用程序里定义了下列类型:
struct Point
{
int x;
int y;
….操作函数….
};
上面这个Point不是基本类型,但它拷贝时也不会抛出意外。你可以用类型特性这个机制来告知这个信息。你所要做的就是再次打开TypeTraits命名空间然后在里面放入一个CopyThrows的显式实例化(explicit instantiation)版本
//在file"Pint.h"文件中,Point的定义后
namespace TypeTraits
{
template <> struct CopyThrows<Point>
{
enum {value = false };
};
}
更好的是,你可以为一整个类型种类定制CopyThrows,这通过部分模板特化(partial template specialization)来实现,考虑一下标准复数类型,std::complex<T>,你可以用基本算术类型实例化std::complex,但也可以用用户定义算术类型,比如Rational或BrigInt。现在,因为拷贝一个std::complex<T>对象包括拷贝两个T对象(实数部分和虚数部分),由此可知std::complex<T>有着和T一样的CopyThrows特性。你能够通过下面代码表示这一点:
namespace TypeTraits
{
template <class T> struct CopyThrows<std::complex<T> >
{
enum { value = CopyThrows<T>::value };
};
}
我们回到buffer。buffer如何使用CopyThrows的信息?通过使用Int2Type模板类[5][6]在编译时分派一个布尔值是非常简单的。回忆一下,Int2Type的定义有着简单的表象。
Template <int v>
Struct Int2Type
{
enum { value = v };
};
下面是buffer的构造函数怎样使用Int2Type来分派调用一个意外安全或意外无关的初始化函数的例子:
template <class T, class Allocator>
std::allocator<T> >
class buffer : private Allocator
{
private:
enum { copyThrow =
TypeTraits::CopyThrows<T>::value != 0 };
//无意外初始化
void Init(size_type n, const T& value,
Loki::Int2Type<false>)
{
….
}
//有可能出现意外的初始化
void Init(size_type n, const T& value,
Loki::Int2Type<true>)
{
….
}
public
explicit buffer(size_type m,
const T& value = T(),
const Allocator& a = Allocator())
{
Init(n, value,
Loki::Int2Type(copyThrows>());
}
};
其它buffer可能会需要的信息包括:
* 类型是否为MemCopyable,就是说,拷贝一个对象和memcpy这个对象的字节结果一样。显然,基本类型和POD结构(简单旧数据,C风格)属于这种情况。
* 类型是否为MemMoveable,就是说,在内存中从一个地方拷贝一个对象到另一个地方并且摧毁源对象,结果和memcpy对象的字节而且没有摧毁源对象一致。再一次的,基本类型和POD属于这种情况。但是,你会很快看到,有相当多的用户定义类型是MemMoveable的。
“ 下一部分的“泛型<编程>”会定义MemCopyable和MemMoveable并且用和CopyThrows类似的方式使用它们。关于MemCopyable和MemMoveable是否就此结束了?根本不是。它们和供下载代码中的reallocate和inplace_reallocate一样,都意味着我们面临内存分配的挑战。到那时——会有详尽说明!
鸣谢:
对Tim Sharrock的详尽检查非常感谢。
参考数目和注释
[1] Herb Sutter. Exceptional C++ (Addison-Wesley, 2000).
[2] COW 爱好者俱乐部的缩写. COW 是写时拷贝的缩写. COW是可以使std::vector所用的用拷贝实现移动原则开销较小的策略。但是,许多库的作者正在去掉基于COW的实现,因为这在多线程程序中会引起问题。
[3] 总的来说, buffer必须和vector有同样级别的意外。参看Bjarne Stroustrup, Appendix E: Standard-Library Exception Safety 在 <www.research.att.com/~bs/3rd_safe0.html>.
[4] Boost 是尖端的C++库集, 参看 <www.boost.org>.
[5] Loki 是Modern C++ Design (Addison-Wesley, 2001)一书附带库,实际上是你们大家参与一起写成的.你能从 <www.moderncppdesign.com>下载下载Loki.
[6] Andrei Alexandrescu. "Generic<Programming>: Mappings between Types and Values," C/C++ Users Journal C++ Experts Forum, October 2000, <www.cuj.com/experts/1810/alexandr.htm>.
Andrei Alexandrescu 是位于西雅图的华盛顿大学的博士生,也是受到好评的《Modern C++ Design》一书的作者。可以通过www.moderncppdesign.com. 来联系他。Andrei同时也是C++研讨会 (<www.gotw.ca/cpp_seminar>).的一名有号召力的讲师。
你可以从CUJ网站或http://merced.go.nease.net/code/buffer.zip获得本文源代码。
Posted: October 26, 2006 at 5:34 pm | Tags: cache, html, java, linux, php, server, web, 优化, 开发, 技术, 类, 缓存
一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站所能比拟的。
大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。
上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。
1、HTML静态化
其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。
除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。
同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。
2、图片服务器分离
大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃,在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule,保证更高的系统消耗和执行效率。
3、数据库集群和库表散列
大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。
在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。
上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。
4、缓存
缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。
架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。
网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多了,.net不是很熟悉,相信也肯定有。
5、镜像
镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等工具。
6、负载均衡
负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。
负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。
硬件四层交换
第四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。 第四层交换功能就象是虚IP,指向物理服务器。它传输的业务服从的协议多种多样,有HTTP、FTP、NFS、Telnet或其他协议。这些业务在物理服务器基础上,需要复杂的载量平衡算法。在IP世界,业务类型由终端TCP或UDP端口地址来决定,在第四层交换中的应用区间则由源端和终端IP地址、TCP和UDP端口共同决定。
在硬件四层交换产品领域,有一些知名的产品可以选择,比如Alteon、F5等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。Yahoo中国当初接近2000台服务器使用了三四台Alteon就搞定了。
软件四层交换
大家知道了硬件四层交换机的原理后,基于OSI模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。
软件四层交换我们可以使用Linux上常用的LVS来解决,LVS就是Linux Virtual Server,他提供了基于心跳线heartbeat的实时灾难应对解决方案,提高系统的鲁棒性,同时可供了灵活的虚拟VIP配置和管理功能,可以同时满足多种应用需求,这对于分布式的系统来说必不可少。
一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。这样的架构我准备空了专门详细整理一下和大家探讨。
对于大型网站来说,前面提到的每个方法可能都会被同时使用到,我这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会,有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大,希望大家一起讨论,达到抛砖引玉之效。
Posted: October 26, 2006 at 4:10 pm | Tags: cache, html, linux, server, unix, windows, 优化, 开发, 技术, 类, 缓存
应用加速(Application Acceleration)是当前信息产业中流行的一个新的时髦词语,但它到底是什么含义呢?
缓慢的应用软件响应时间可能会让用户感到失望并且失去生产力,从而对公司的核心实力造成影响,所以,应用软件加速成为信息产业中的一个新的时髦词语也就不足为奇了,但 它到底是什么含义呢?您是否需要它呢?如果需要的话,那么最具扩展性的实现方式是什么呢?让我们来看看不同的应用软件加速技术以及如何创建应用软件加速策略来与您的公 司一同成长。
本地VS网络应用软件
在过去,大部分应用软件都是安装并运行在本地计算机,现在的小型公司也通常是这样。对于本地应用软件而言,性能主要取决于本地系统资源,它受到处理器速度、处理器数量 、物理和虚拟内存数量和共享这些资源的其他应用软件(多任务)的影响。
对于这种类型的系统环境,提高应用软件性能是在单独的机器上通过升级硬件、关闭消耗宝贵资源的视觉效果,禁用不必要的与您的应用软件争夺处理器时间和内存的后台服务, 使用预取(prefetching)技术来提高应用软件的加载时间,定期整理磁盘碎片,优化各种设置比如设定进程的优先级、调整与性能相关的注册表条目等等。
然而,在当今的大中型企业中,很多应用软件都是通过局域网、城域网或互联网传递的,这对原有的方法带来了新的问题,同时也为网络带宽和网络协议带来了新的问题。
应用软件转移到了互联网上
越来越多的应用软件正在通过互联网来传递,从便利性的角度来看,这很有意义,因为几乎所有与互联网相连的计算机都有网络浏览器,这就排除了在用户系统上安装任何软件的 需要,HTTP是最普遍的网络协议中的一个,这种应用软件的“互联网化”为用户创建了一个通用界面和环境,同时也是系统管理员的通用传递系统。
在基于网络的应用软件加速的问题上,一个关键因素是使用一个逆向代理(reverse proxy)来缓存静态对象,我们在这个栏目里所讨论的缓冲方案在以往更多地被应用于正向缓存 ,这是一种加速内部用户访问互联网内容的方法:让代理服务器在本地存储从互联网站点上下载的对象。
逆向代理,正如名字所暗示的,是反方向工作的,逆向代理服务器存储了在您的内部服务器上被访问过的对象,然后将它们返回给请求者,这样请求者不必到服务器上去检索这些 数据,对于外部用户而言就意味着性能的提高。
扩展一个逆向代理的解决方案
互联网应用软件加速经常是在企业环境下讨论的,但即使是小型公司也可以使用逆向服务器来加速基于互联网的应用软件,您不必花费很多钱来实现这一点,免费的和廉价的逆向 代理软件包括:
Squid:一个基于GPL许可的开源的代理服务器,它可以从ftp://ftp.squid-cache.org/pub/上免费下载,它 可以在Linux、FreeBSD、Mac OS X和很多版本的UNIX上运行,您可以使用Cygwin开发环境(Cygwin development environment)在Windows上编译和运行Squid。
IIS逆向代理:一个用于互联网信息服务(IIS)的开源逆向代理软件,它是基于MIT许可的,可以安装在运行IIS的Windows2000、XP或Windows Server2003上,您可以在http://www.saltypickle.com/Home/16上下载到这一软件。
Orenosp安全逆向代理:一个逆向代理的共享软件,还具有平衡负载和安全端口的转发功能,它可以运行在Windows NT、2000、XP和2003,以及Linux和Mac OS X上,如果需要 更多的信息可以参见http://hp.vector.co.jp/authors/VA027031/orenosp/index_en.html
通常,随着公司的发展,您将会需要对互联网应用软件进行加速,功能不复杂的逆向代理服务器会缓存所有的数据,而高级一些的会允许您像外科手术般地来操作哪些数据需要缓 存,哪些不需要。您可能还希望考虑可扩展性的协议支持:您需要缓存的可能不仅仅是HTTP对象。
从免费的和低廉的解决方案中提升一步,您可以发现一些适合大中型公司需求的中等价位的产品,例如:
微软的ISA服务器:ISA可以作为正向的或逆向代理服务器和应用层过滤防火墙,所以您可以应用很多相关的功能。标准版可以运行在Windows 2000 Server或Windows Server 2003上,每个处理器的价格为1499美元,企业版可以部署在服务器阵列上,因而具备更强的灵活性与可扩展性,每个处理器价格为5999美元。需要更多的信息,请点击此处。
Vigos AG网站加速器:对于仅仅需要一个逆向代理而不需要ISA的附加功能的用户而言,这是一个提高他们网络站点性能的选择。Vigos还可以实时压缩发送的数据,“简易” 版(支持单一域)的价格是499美元,标准版(支持最多十个域)的价格是999美元,企业版(支持无限个域)的价格为1999美元。需要更多的信息,请点击此处。在企业级,逆向代理允许您对多个前端互联网服务器实施逆向代理方案,并提供SSL的安全连接保障。为企业级市场设计的高端逆向代理解决方案需要花费30000美元或更多,一些 可选方案包括:
Blue Coat Proxy SG:作为一套配置齐全立即可用的设备,Proxy SG有很多产品系列(400, 800和8000)以满足不通的预算和需求,您可以对上游的互联网服务器实施正向缓存 ,甚至对上游的服务器进行状态检查,高端的8000系列可以支持超过50000接入用户并支持最高300Mbps的城域网吞吐量,型号为8000-4的价格为99000美元,需要更多的信息,请点击此处。
思科内容引擎(Content Engine):这是Cisco的缓存与内容过滤设备,可以部署在您的互联网站点之前来缓存或分流请求,以及卸载互联网服务器的数据流量,以此来增加网 络应用软件的性能。内容引擎7325的价格在40000美元到50000之间。
Posted: October 26, 2006 at 11:06 am | Tags: blog, class, html, java, linux, python, unix, windows, 优化, 平台, 开发, 技术, 类
声明:
.本文2004年5月首发于《CSDN开发高手》,版权归该杂志与《程序员》杂志社所有。杂志限于篇幅部分内容有所删节,此处版本为相对完整版本。
.本文为介绍性文章,会随笔者学习C++语言不断更新。
库
在C++中,库的地位是非常高的。C++之父 Bjarne Stroustrup先生多次表示了设计库来扩充功能要好过设计更多的语法的言论。现实中,C++的库门类繁多,解决的问题也是极其广泛,库从轻量级到重量级的都有。不少都是让人眼界大开,亦或是望而生叹的思维杰作。由于库的数量非常庞大,而且限于笔者水平,其中很多并不了解。所以文中所提的一些库都是比较著名的大型库。
标准库
标准库中提供了C++程序的基本设施。虽然C++标准库随着C++标准折腾了许多年,直到标准的出台才正式定型,但是在标准库的实现上却很令人欣慰得看到多种实现,并且已被实践证明为有工业级别强度的佳作。
1、 Dinkumware C++ Library
参考站点:http://www.dinkumware.com/
P.J. Plauger编写的高品质的标准库。P.J. Plauger博士是Dr. Dobb’s程序设计杰出奖的获得者。其编写的库长期被Microsoft采用,并且最近Borland也取得了其OEM的license,在其C/C++的产品中采用Dinkumware的库。
2、 RogueWave Standard C++ Library
参考站点:http://www.roguewave.com/
这个库在Borland C++ Builder的早期版本中曾经被采用,后来被其他的库给替换了。笔者不推荐使用。
3、SGI STL
参考站点:http://www.roguewave.com/
SGI公司的C++标准模版库。
4、STLport
参考站点:http://www.stlport.org/
SGI STL库的跨平台可移植版本。
准标准库——Boost
Boost库是一个经过千锤百炼、可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的发动机之一。 Boost库由C++标准委员会库工作组成员发起,在C++社区中影响甚大,其成员已近2000人。 Boost库为我们带来了最新、最酷、最实用的技术,是不折不扣的“准”标准库。
Boost中比较有名气的有这么几个库:
Regex
正则表达式库
Spirit
LL parser framework,用C++代码直接表达EBNF
Graph
图组件和算法
Lambda
在调用的地方定义短小匿名的函数对象,很实用的functional功能
concept check
检查泛型编程中的concept
Mpl
用模板实现的元编程框架
Thread
可移植的C++多线程库
Python
把C++类和函数映射到Python之中
Pool
内存池管理
smart_ptr
5个智能指针,学习智能指针必读,一份不错的参考是来自CUJ的文章:
Smart Pointers in Boost,哦,这篇文章可以查到,CUJ是提供在线浏览的。中文版见笔者在《Dr. Dobb’s Journal软件研发杂志》第7辑上的译文。
Boost总体来说是实用价值很高,质量很高的库。并且由于其对跨平台的强调,对标准C++的强调,是编写平台无关,现代C++的开发者必备的工具。但是Boost中也有很多是实验性质的东西,在实际的开发中实用需要谨慎。并且很多Boost中的库功能堪称对语言功能的扩展,其构造用尽精巧的手法,不要贸然的花费时间研读。Boost另外一面,比如Graph这样的库则是具有工业强度,结构良好,非常值得研读的精品代码,并且也可以放心的在产品代码中多多利用。
参考站点:http://www.boost.org(国内镜像:http://www.c-view.org/tech/lib/boost/index.htm)
GUI
在众多C++的库中,GUI部分的库算是比较繁荣,也比较引人注目的。在实际开发中,GUI库的选择也是非常重要的一件事情,下面我们综述一下可选择的GUI库,各自的特点以及相关工具的支持。
1、 MFC
大名鼎鼎的微软基础类库(Microsoft Foundation Class)。大凡学过VC++的人都应该知道这个库。虽然从技术角度讲,MFC是不大漂亮的,但是它构建于Windows API 之上,能够使程序员的工作更容易,编程效率高,减少了大量在建立 Windows 程序时必须编写的代码,同时它还提供了所有一般 C++ 编程的优点,例如继承和封装。MFC 编写的程序在各个版本的Windows操作系统上是可移植的,例如,在 Windows 3.1下编写的代码可以很容易地移植到 Windows NT 或 Windows 95 上。但是在最近发展以及官方支持上日渐势微。
2、 QT
参考网站:http://www.trolltech.com/
Qt是Trolltech公司的一个多平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所用功能。Qt是完全面向对象的很容易扩展,并且允许真正地组件编程。自从1996年早些时候,Qt进入商业领域,它已经成为全世界范围内数千种成功的应用程序的基础。Qt也是流行的Linux桌面环境KDE 的基础,同时它还支持Windows、Macintosh、Unix/X11等多种平台。
3、WxWindows
参考网站:http://www.wxwindows.org/
跨平台的GUI库。因为其类层次极像MFC,所以有文章介绍从MFC到WxWindows的代码移植以实现跨平台的功能。通过多年的开发也是一个日趋完善的GUI库,支持同样不弱于前面两个库。并且是完全开放源代码的。新近的C++ Builder X的GUI设计器就是基于这个库的。
4、Fox
开放源代码的GUI库。作者从自己亲身的开发经验中得出了一个理想的GUI库应该是什么样子的感受出发,从而开始了对这个库的开发。有兴趣的可以尝试一下。
参考网站:http://www.fox-toolkit.org/
5、 WTL
基于ATL的一个库。因为使用了大量ATL的轻量级手法,模板等技术,在代码尺寸,以及速度优化方面做得非常到位。主要面向的使用群体是开发COM轻量级供网络下载的可视化控件的开发者。
6、 GTK
参考网站:http://gtkmm.sourceforge.net/
GTK是一个大名鼎鼎的C的开源GUI库。在Linux世界中有Gnome这样的杀手应用。而GTK就是这个库的C++封装版本。
网络通信
ACE
参考网站:http://www.cs.wustl.edu/~schmidt/ACE.html
C++库的代表,超重量级的网络通信开发框架。ACE自适配通信环境(Adaptive Communication Environment)是可以自由使用、开放源代码的面向对象框架,在其中实现了许多用于并发通信软件的核心模式。ACE提供了一组丰富的可复用C++包装外观(Wrapper Facade)和框架组件,可跨越多种平台完成通用的通信软件任务,其中包括:事件多路分离和事件处理器分派、信号处理、服务初始化、进程间通信、共享内存管理、消息路由、分布式服务动态(重)配置、并发执行和同步,等等。
StreamModule
参考网站:http://www.omnifarious.org/StrMod/
设计用于简化编写分布式程序的库。尝试着使得编写处理异步行为的程序更容易,而不是用同步的外壳包起异步的本质。
SimpleSocket
参考网站:http://home.hetnet.nl/~lcbokkers/simsock.htm
这个类库让编写基于socket的客户/服务器程序更加容易。
A Stream Socket API for C++
参考网站:http://www.pcs.cnu.edu/~dgame/sockets/socketsC++/sockets.html
又一个对Socket的封装库。
XML
Xerces
参考网站:http://xml.apache.org/xerces-c/
Xerces-C++ 是一个非常健壮的XML解析器,它提供了验证,以及SAX和DOM API。XML验证在文档类型定义(Document Type Definition,DTD)方面有很好的支持,并且在2001年12月增加了支持W3C XML Schema 的基本完整的开放标准。
XMLBooster
参考网站:http://www.xmlbooster.com/
这个库通过产生特制的parser的办法极大的提高了XML解析的速度,并且能够产生相应的GUI程序来修改这个parser。在DOM和SAX两大主流XML解析办法之外提供了另外一个可行的解决方案。
Pull Parser
参考网站:http://www.extreme.indiana.edu/xgws/xsoap/xpp/
这个库采用pull方法的parser。在每个SAX的parser底层都有一个pull的parser,这个xpp把这层暴露出来直接给大家使用。在要充分考虑速度的时候值得尝试。
Xalan
参考网站:http://xml.apache.org/xalan-c/
Xalan是一个用于把XML文档转换为HTML,纯文本或者其他XML类型文档的XSLT处理器。
CMarkup
参考网站:http://www.firstobject.com/xml.htm
这是一种使用EDOM的XML解析器。在很多思路上面非常灵活实用。值得大家在DOM和SAX之外寻求一点灵感。
libxml++
http://libxmlplusplus.sourceforge.net/
libxml++是对著名的libxml XML解析器的C++封装版本
科学计算
Blitz++
参考网站:http://www.oonumerics.org/blitz/
Blitz++ 是一个高效率的数值计算函数库,它的设计目的是希望建立一套既具像C++ 一样方便,同时又比Fortran速度更快的数值计算环境。通常,用C++所写出的数值程序,比 Fortran慢20%左右,因此Blitz++正是要改掉这个缺点。方法是利用C++的template技术,程序执行甚至可以比Fortran更快。Blitz++目前仍在发展中,对于常见的SVD,FFTs,QMRES等常见的线性代数方法并不提供,不过使用者可以很容易地利用Blitz++所提供的函数来构建。
POOMA
参考网站:http://www.codesourcery.com/pooma/pooma
POOMA是一个免费的高性能的C++库,用于处理并行式科学计算。POOMA的面向对象设计方便了快速的程序开发,对并行机器进行了优化以达到最高的效率,方便在工业和研究环境中使用。
MTL
参考网站:http://www.osl.iu.edu/research/mtl/
Matrix Template Library(MTL)是一个高性能的泛型组件库,提供了各种格式矩阵的大量线性代数方面的功能。在某些应用使用高性能编译器的情况下,比如Intel的编译器,从产生的汇编代码可以看出其与手写几乎没有两样的效能。
CGAL
参考网站:www.cgal.org
Computational Geometry Algorithms Library的目的是把在计算几何方面的大部分重要的解决方案和方法以C++库的形式提供给工业和学术界的用户。
游戏开发
Audio/Video 3D C++ Programming Library
参考网站:http://www.galacticasoftware.com/products/av/
AV3D是一个跨平台,高性能的C++库。主要的特性是提供3D图形,声效支持(SB,以及S3M),控制接口(键盘,鼠标和遥感),XMS。
KlayGE
参考网站:http://home.g365.net/enginedev/
国内游戏开发高手自己用C++开发的游戏引擎。KlayGE是一个开放源代码、跨平台的游戏引擎,并使用Python作脚本语言。KlayGE在LGPL协议下发行。感谢龚敏敏先生为中国游戏开发事业所做出的贡献。
OGRE
参考网站:http://www.ogre3d.org
OGRE(面向对象的图形渲染引擎)是用C++开发的,使用灵活的面向对象3D引擎。它的目的是让开发者能更方便和直接地开发基于3D硬件设备的应用程序或游戏。引擎中的类库对更底层的系统库(如:Direct3D和OpenGL)的全部使用细节进行了抽象,并提供了基于现实世界对象的接口和其它类。
线程
C++ Threads
参考网站:http://threads.sourceforge.net/
这个库的目标是给程序员提供易于使用的类,这些类被继承以提供在Linux环境中很难看到的大量的线程方面的功能。
ZThreads
参考网站:http://zthread.sourceforge.net/
一个先进的面向对象,跨平台的C++线程和同步库。
序列化
s11n
参考网站:http://s11n.net/
一个基于STL的C++库,用于序列化POD,STL容器以及用户定义的类型。
Simple XML Persistence Library
参考网站:http://sxp.sourceforge.net/
这是一个把对象序列化为XML的轻量级的C++库。
字符串
C++ Str Library
参考网站:http://www.utilitycode.com/str/
操作字符串和字符的库,支持Windows和支持gcc的多种平台。提供高度优化的代码,并且支持多线程环境和Unicode,同时还有正则表达式的支持。
Common Text Transformation Library
参考网站:http://cttl.sourceforge.net/
这是一个解析和修改STL字符串的库。CTTL substring类可以用来比较,插入,替换以及用EBNF的语法进行解析。
GRETA
参考网站:http://research.microsoft.com/projects/greta/
这是由微软研究院的研究人员开发的处理正则表达式的库。在小型匹配的情况下有非常优秀的表现。
综合
P::Classes
参考网站:http://pclasses.com/
一个高度可移植的C++应用程序框架。当前关注类型和线程安全的signal/slot机制,i/o系统包括基于插件的网络协议透明的i/o架构,基于插件的应用程序消息日志框架,访问sql数据库的类等等。
ACDK – Artefaktur Component Development Kit
参考网站:http://acdk.sourceforge.net/
这是一个平台无关的C++组件框架,类似于Java或者.NET中的框架(反射机制,线程,Unicode,废料收集,I/O,网络,实用工具,XML,等等),以及对Java, Perl, Python, TCL, Lisp, COM 和 CORBA的集成。
dlib C++ library
参考网站:http://www.cis.ohio-state.edu/~kingd/dlib/
各种各样的类的一个综合。大整数,Socket,线程,GUI,容器类,以及浏览目录的API等等。
Chilkat C++ Libraries
参考网站:http://www.chilkatsoft.com/cpp_libraries.asp
这是提供zip,e-mail,编码,S/MIME,XML等方面的库。
C++ Portable Types Library (PTypes)
参考网站:http://www.melikyan.com/ptypes/
这是STL的比较简单的替代品,以及可移植的多线程和网络库。
LFC
参考网站:http://lfc.sourceforge.net/
哦,这又是一个尝试提供一切的C++库
其他库
Loki
参考网站:http://www.moderncppdesign.com/
哦,你可能抱怨我早该和Boost一起介绍它,一个实验性质的库。作者在loki中把C++模板的功能发挥到了极致。并且尝试把类似设计模式这样思想层面的东西通过库来提供。同时还提供了智能指针这样比较实用的功能。
ATL
ATL(Active Template Library)是一组小巧、高效、灵活的类,这些类为创建可互操作的COM组件提供了基本的设施。
FC++: The Functional C++ Library
这个库提供了一些函数式语言中才有的要素。属于用库来扩充语言的一个代表作。如果想要在OOP之外寻找另一分的乐趣,可以去看看函数式程序设计的世界。大师Peter Norvig在 “Teach Yourself Programming in Ten Years”一文中就将函数式语言列为至少应当学习的6类编程语言之一。
FACT!
参考网站:http://www.kfa-juelich.de/zam/FACT/start/index.html
另外一个实现函数式语言特性的库
Crypto++
提供处理密码,消息验证,单向hash,公匙加密系统等功能的免费库。
还有很多非常激动人心或者是极其实用的C++库,限于我们的水平以及文章的篇幅不能包括进来。在对于这些已经包含近来的库的介绍中,由于并不是每一个我们都使用过,所以难免有偏颇之处,请读者见谅。
书籍
以前熊节先生曾撰文评论相对于Java程序设计语言,C++的好书多如牛毛。荣耀先生在《程序员》杂志上撰文《C++程序设计之四书五经》也将本领域内几乎所有的经典书籍作了全面的介绍,任何关于书的评论此时看来便是很多余的了。个人浅见,除非你打算以C++作为唯一兴趣或者生存之本,一般读者确实没有足够的时间和必要将20余本书籍全部阅读。更有参考价值的是荣耀先生的另一篇文章:《至少应该阅读的九本C++著作》,可以从下面的地址浏览到此文:
http://www.royaloo.com/articles/articles_2003/9CppBooks.htm
下面几本书对于走在C++初学之路上的读者是我们最愿意推荐给大家的:
《C++ Primer》
哦,也许你会抱怨我们为什么不先介绍TCPL,但对于走在学习之路上的入门者,本书内容更为全面,更为详细易懂,我们称它为“C++的超级宝典”并不过分。配有一本不错的习题解答《C++ Primer Answer Book》可以辅助你的学习之路。
《Essential C++》
如果说《C++ Primer》是C++领域的超级宝典,那么此书作为掌握C++的大局观当之无愧。正如《.NET大局观》一书能够让读者全揽.NET,本书讲述了C++中最核心的全部主题。书虽不厚,内容精炼,不失为《C++ Primer》读者茶余饭后的主题回顾之作。
《The C++ Programming Language》
Bjarne为你带来的C++教程,真正能够告诉你怎么用才叫真正的C++的唯一一本书。虽然如同“某某程序设计语言”这样的书籍会给大家一个内容全揽,入门到精通的感觉,但本书确实不太适合初学者阅读。如果你自认为是一名很有经验的C++程序员,那至少也要反复咀嚼Bjarne先生所强调的若干内容。
《Effective C++》,《More Effective C++》
是的,正如一些C++爱好者经常以读过与没有读过上述两本作品来区分你是否是C++高手。我们也极力推崇这两本著作。在各种介绍C++专家经验的书籍里面,这两本是最贴近语言本质,看后最能够有脱胎换骨感觉的书,读此书你需每日三省汝身。
技术书籍仁者见仁,过多的评论反无太多意义,由读者喜好选择最适合自己的书方为上策。
资源网站
正如我们可以通过计算机历史上的重要人物了解计算机史的发展,C++相关人物的网站也可以使我们得到最有价值的参考与借鉴,下面的人物我们认为没有介绍的必要,只因下面的人物在C++领域的地位众所周知,我们只将相关的资源进行罗列以供读者学习,他们有的工作于贝尔实验室,有的工作于知名编译器厂商,有的在不断推进语言的标准化,有的为读者撰写了多部千古奇作……
Bjarne Stroustrup http://www.research.att.com/~bs/
Stanley B. Lippman
http://blogs.msdn.com/slippman/(中文版http://www.zengyihome.net/slippman/index.htm)
Scott Meyers http://www.aristeia.com/
David Musser http://www.cs.rpi.edu/~musser/
Bruce Eckel http://www.bruceeckel.com
Nicolai M. Josuttis http://www.josuttis.com/
Herb Sutter http://www.gotw.ca/
Andrei Alexandrescu http://www.moderncppdesign.com/
侯捷先生 http://www.jjhou.com
孟岩先生 先生繁忙于工作,痴迷于技术,暂无个人主页,关于先生的作品可以通过CSDN的专栏和侯先生的主页访问到。
荣耀先生 http://www.royaloo.com/
潘爱民先生 http://www.icst.pku.edu.cn/panaimin/pam_homepage.htm
除了上述大师的主页外,以下的综合类C++学习参考站点是我们非常愿意向大家推荐的:
CodeProject http://www.codeproject.com
CodeGuru http://www.codeguru.com
Dr. Dobb’s Journal http://www.ddj.com
C/C++ Users Journal http://www.cuj.com
C维视点 http://www.c-view.org
allaboutprogram http://www.allaboutprogram.com
其他资料
ISO IEC JTC1/SC22/WG21 – C++:标准C++的权威参考
http://anubis.dkuug.dk/jtc1/sc22/wg21/
C++ FAQ LITE — Frequently Asked Questions: 最为全面的C++FAQ
http://www.sunistudio.com/cppfaq/index.html
C/C++ 新闻组:
你不妨尝试从这里提问和回答问题,很多不错的Q&A资源……
.alt.comp.lang.learn.c-c++
这个简单些,如果你和我一样是个菜鸟
.comp.lang.c++.moderated
嗯,这个显然水平高一些
.comp.std.c++
如果你需要讨论标准C++相关话题的话
不得不写的结束语
结束的时候也是总结现状,展望未来的时候。虽然C++从脱胎于C开始,一路艰难坎坷的走过来,但是无论如何C++已经取得了工业基础的地位。文章列举的大量相关资源就是最好的证明,而业界的大量用C++写成的产品代码以及大量的C++职业工程师则是最直接的证明。同时,我们可以看到各个高校的计算机专业都开设有C++这门课程,网络上对于C++的学习讨论也从来都没有停过。但是,在Java和.NET两大企业开发平台的围攻下,给人的感觉是C++越来越“不行”了。
C++在面向企业的软件开发中,在开发便捷性等方面的确要比Java和C#差很多,其中一个问题是C++语言本身比较复杂,学习曲线比较陡峭,另外一个问题是C++标准化的时间太长,丧失了很多的壮大机会,耗费了很多精力在厂商的之间的斗争上,而C++的标准库离一个完善的程序开发框架还缺少太多太多的内容,各个第三方的类库和框架又在一致性和完整性上没法和随平台提供的框架相提并论。难道C++真的要退出历史舞台了?
从C++目前的活跃程度,以及应用现状来说是完全能够肯定C++仍然是软件工业的基础,也不会退出历史舞台的。另外从Boost,Loki这些库中我们也能够看到C++的发展非常活跃,对于新技术新思维非常激进,C++仍然广泛受到关注。从ACE在高性能通信领域的应用,以及MTL这样的库在数值计算领域的出色表现,我们可以看到C++在高性能应用场合下的不可替代的作用,而嵌入式系统这样的内存受限开发平台,比如Symbian OS上,C++已经发挥着并且将发挥更大的作用。可以预见的是以后的软件无论上层的应用怎么变,它的底层核心都会是由C/C++这样的系统级软件编写的,比如Java虚拟机,.NET Framwork。因为只有这样的系统级软件才能完全彻底的发挥机器的功能。
需要看到的是两个趋势,一个趋势是C++变得更加复杂,更加学院派,通过模板等有潜力的语法因素构造越来越精巧的库成为了现代C++的热点,虽然在利用库实现新的编程范式,乃至设计模式等方面很有开创意义,也确实产生了一些能够便捷开发的工具,但是更多的是把C++变得更加强大,更加复杂,也更加难懂,似乎也更加学院派,不得不说它正在向边缘化道路发展。另一个趋势是C++在主流的企业应用开发中已经逐渐退出了,ERP这样的企业软件开发中基本上不会考虑C++,除非需要考虑性能或者和遗留代码的集成这些因素。C++退守到系统级别语言,成为软件工业的基础是大势所趋。然而反思一下,真的是退守么?自从STL出现,无数的人风起云涌的开始支持C++,他们狂呼“我看到深夜消失了,目标软件工程的出现。我看到了可维护的代码。”是的,STL在可维护性下做得如此出色。但是又怎样呢?STL为C++铺平了现代软件工程的道路,而在上层应用程序软件开发领域这块场地早不单独属于C++,很多程序设计语言都做得很出色,疯狂的支持者会毫不犹豫地说我们应当支持C++,因为它是世界上最棒的语言。而坦率地说,你的腰杆真的那么硬么?也许只是在逃避一些事实。C++是优秀的,这不可否认,STL的出现让C++一度走上了最辉煌的时刻,然而现在看来……我的一位恩师曾言:真正能够将STL应用得淋漓尽致的人很保守地说国内也不超过200人,或许不加入STL能够使C++向着它应当发展的方向发展的更好,而现在看来,C++也应当回首到真正属于他的那一片圣地上……