Hunter的大杂烩

十一月 17, 2005

在 Linux 中使用共享对象

类归于: C++, Linux, 技术话题, 闲聊 — hunter @ 3:12 下午

内容:

示例程序

运用共享内存

参考资料

参考资料

关于作者

对本文的评价

相关内容:

用 Eclipse 平台进行 C/C++ 开发

Java programming for C/C++ developers

developerWorks Toolbox subscription

订阅:

developerWorks 时事通讯

让共享内存为您服务,而不是为您制造麻烦
级别: 初级

Sachin O. Agrawal
资深软件工程师, IBM Software Labs India
2004 年 5 月

充分利用共享内存并不总是容易的。在本文中,IBM 的 Sachin Agrawal 与我们共享了他的 C++ 专门技术,展示了面向对象如何去利用一个独特而实用的进程间通信通道的关键优势。
就时间和空间而言,共享内存可能是所有现代操作系统都具备的最高效的进程间通信通道。 共享内存同时将地址空间映射到多个进程:一个进程只需依附到共享内存并像使用普通内存一样使用它,就可以开始与其他进程进行通信。

不过,在面向对象编程领域中,进程更倾向于使用共享对象而不是原始的信息。通过对象,不需要再对对象中容纳的信息进行序列化、传输和反序列化。共享对象也驻留在共享内存中,尽管这种对象“属于”创建它们的进程,但是系统中的所有进程都可以访问它们。因此,共享对象中的所有信息都应该是严格与特定进程无关的。

这与当前所有流行的编译器所采用的 C++ 模型是直接矛盾的:C++ 对象中总是包含指向各种 Vee-Table 和子对象的指针,这些是 与特定进程相关的。要让这种对象可以共享,您需要确保在所有进程中这些指针的目标都驻留在相同的地址。

在一个小的示例的帮助下,本文展示了在哪些情况下 C++ 模型可以成功使用共享内存模型,哪些情况下不能,以及可能从哪里着手。讨论和示例程序都只限于非静态数据成员和虚函数。其他情形不像它们这样与 C++ 对象模型关系密切:静态的和非静态非虚拟的成员函数在共享环境中没有任何问题。每个进程的静态成员不驻留在共享内存中(因此没有问题),而共享的静态成员的问题与这里讨论到的问题类似。

环境假定

本文仅局限于用于 32 位 x86 Interl 体系结构的 Red Hat Linux 7.1,使用版本 2.95 的 GNU C++ 编译器及相关工具来编译和测试程序。不过,您同样可以将所有的思想应用到任意的机器体系结构、操作系统和编译器组合。

示例程序

示例程序由两个客户机构成:shm_client1 和 shm_client2,使用由共享对象库 shm_server 提供的共享对象服务。对象定义在 common.h 中:

清单 1. common.h 中的定义

#ifndef __COMMON_H__
#define __COMMON_H__

class A {
public:
int m_nA;
virtual void WhoAmI();

static void * m_sArena;
void * operator new (unsigned int);
};

class B : public A {
public:
int m_nB;
virtual void WhoAmI();
};

class C : virtual public A {
public:
int m_nC;
virtual void WhoAmI();
};

void GetObjects(A ** pA, B ** pB, C ** pC);

#endif //__COMMON_H__

清单 1 定义了三个类(A、B 和 C),它们有一个共同的虚函数 WhoAmI() 。基类 A 有一个名为 m_nA 的成员。定义静态成员 m_sArena 和重载操作 new() 是为了可以在共享内存中构造对象。类 B 简单地从 A 继承,类 C 从 A 虚拟地继承。为了确保 A、B 和 C 的大小明显不同,定义了 B::m_nB 和 C::m_nC 。这样就简化了 A::operator new() 的实现。 GetObjects() 接口返回共享对象的指针。

共享库的实现在 shm_server.cpp 中:

清单 2. 库 – shm_server.cpp

#include
#include
#include
#include
#include
#include

#include “common.h”

void * A::m_sArena = NULL;

void * A::operator new (unsigned int size)
{
switch (size)
{
case sizeof(A):
return m_sArena;

case sizeof(B):
return (void *)((int)m_sArena + 1024);

case sizeof(C):
return (void *)((int)m_sArena + 2048);

default:
cerr < < __FILE__ << ":" << __LINE__ << " Critical error" <<
endl;
}
}

void A::WhoAmI() {
cout << "Object type: A" << endl;
}

void B::WhoAmI() {
cout << "Object type: B" << endl;
}

void C::WhoAmI() {
cout << "Object type: C" << endl;
}

void GetObjects(A ** pA, B ** pB, C ** pC) {
*pA = (A *)A::m_sArena;
*pB = (B *)((int)A::m_sArena + 1024);
*pC = (C *)((int)A::m_sArena + 2048);
}

class Initializer {
public:
int m_shmid;
Initializer();

static Initializer m_sInitializer;
};

Initializer Initializer::m_sInitializer;

Initializer::Initializer()
{
key_t key = 1234;
bool bCreated = false;

m_shmid = shmget(key, 3*1024, 0666);

if (-1 == m_shmid) {

if (ENOENT != errno) {
cerr << __FILE__ << ":" << __LINE__ << " Critical error" <<
endl;
return;
}

m_shmid = shmget(key, 3*1024, IPC_CREAT | 0666);

if (-1 == m_shmid) {
cerr << __FILE__ << ":" << __LINE__ << " Critical error" <<
endl;
return;
}

cout << "Created the shared memory" << endl;
bCreated = true;
}

A::m_sArena = shmat(m_shmid, NULL, 0);

if (-1 == (int)A::m_sArena) {
cerr << __FILE__ << ":" << __LINE__ << " Critical error" <<
endl;
return;
}

if (bCreated) {
// Construct objects on the shared memory
A * pA;
pA = new A;
pA->m_nA = 1;
pA = new B;
pA->m_nA = 2;
pA = new C;
pA->m_nA = 3;
}

return;
}

让我们更详细地研究清单 2:

第 9-25 行:operator new ()
同一个重载的操作符让您可以在共享内存中构造类 A、B 和 C 的对象。对象 A 直接从共享内存的起始处开始。对象 B 从偏移量 1024 处开始,C 从偏移量 2048 处开始。

第 26-34 行:虚函数
虚函数简单地向标准输出写一行文本。

第 35-39 行:GetObjects
GetObjects() 返回指向共享对象的指针。

第 40-46 行:初始化器(Initializer)
这个类存储共享内存标识符。它的构造函数创建共享内存及其中的对象。如果共享内存已经存在,它就只是依附到现有的共享内存。静态成员 m_sInitializer 确保在使用共享库的客户机模块的 main() 函数之前调用构造函数。

第 48-82 行:Initializer::Initializer()
如果原来没有共享内存,则创建,并在其中创建共享对象。如果共享内存已经存在,对象的构造就会被跳过。 Initializer::m_shmid 记录标识符, A::m_sArena 记录共享内存地址。

即使所有进程都不再使用它了,共享内存也不会被销毁。这样就让您可以显式地使用 ipcs 命令来销毁它,或者使用 ipcs 命令进行一些速查。

客户机进程的实现在 shm_client.cpp 中:

清单 3. 客户机 – shm_client.cpp

#include “common.h”

#include
#include

int main (int argc, char * argv[])
{
int jumpTo = 0;
if (1 < argc) {
jumpTo = strtol(argv[1], NULL, 10);
}

if ((1 > jumpTo) || (6 < jumpTo)) {
jumpTo = 1;
}

A * pA;
B * pB;
C * pC;

GetObjects(&pA, &pB, &pC);

cout << (int)pA << "\t";
cout << (int)pB << "\t";
cout << (int)pC << "\n";

switch (jumpTo) {
case 1:
cout << pA->m_nA < < endl;

case 2:
pA->WhoAmI();

case 3:
cout < < pB->m_nA < < endl;

case 4:
pB->WhoAmI();

case 5:
cout < < pC->m_nA < < endl;

case 6:
pC->WhoAmI();
}

return 0;
}

#include
void DoNothingCode() {
pthread_create(NULL, NULL, NULL, NULL);
}

第 6-35 行
客户机进程获得指向三个共享对象的指针,建立对它们的数据成员的三个引用,并且 — 依赖于命令行的输入 — 调用三个虚函数。

第 36-39 行
没有被调用的 pthread_create() 函数用来强制链接到另一个共享库。来自所有共享库的任何方法都可以满足这一目的。

共享库和客户机可执行文件的两个实例的编译方法如下:

gcc shared g shm_server.cpp o libshm_server.so lstdc++
gcc -g shm_client.cpp -o shm_client1 -lpthread -lshm_server -L .
gcc -g shm_client.cpp -o shm_client2 -lshm_server -L . lpthread

注意,交换了 shm_client1 和 shm_client2 中 shm_server 和 pthread 的链接顺序,以确保 shm_server 共享库在两个可执行文件中的基址不同。可以使用 ldd 命令进一步验证这一点。示例输出通常如下所示:

清单 4. shm_client1 的库映射

ldd shm_client1

libpthread.so.0 => (0×4002d000)
libshm_server.so => (0×40042000)
libc.so.6 => (0×4005b000)
ld-linux.so.2 => (0×40000000)

清单 5. shm_client2 的库映射

ldd shm_client2

libshm_server.so => (0×40018000)
libpthread.so.0 => (0×40046000)
libc.so.6 => (0×4005b000)
ld-linux.so.2 => (0×40000000)

这里的主要目的是使构建的两个客户机二进制文件具有不同的服务器库基址。在这个示例程序的上下文中,使用不被调用的 pthread_create() 函数和不同的共享库链接顺序来达到这一目标。不过,没有具体规则或统一步骤可以作用于所有链接;需要根据不同的情况采取不同的方法。

例 1:shm_client1 与 shm_client1

在下面的输出中,首先在 shell 中调用 shm_client1。由于现在没有共享对象,于是 shm_client1 创建了它们,引用它们的数据成员,调用它们的虚函数,然后退出 — 将对象留在了内存中。第二次,进程只是引用数据成员和虚函数。

清单 6. shm_client1 与 shm_client1 的输出日志

$ ./shm_client1

Created the shared memory
1073844224 1073845248 1073846272
1
Object type: A
2
Object type: B
3
Object type: C

$ ipcs

—— Shared Memory Segments ——–

key shmid owner perms bytes nattch status
0×000004d2 2260997 sachin 666 3072 0

$ ./shm_client1
1073840128 1073841152 1073842176
1
Object type: A
2
Object type: B
-> 0
-> Segmentation fault (core dumped)

当第二个进程试图通过类型 C * 的指针去引用数据成员 A::m_nA 时(您会记得 C 虚拟继承自 A),共享对象内的基子对象(base sub-object)指针会被读取。共享对象是在现在不存在的进程的上下文中构造的。因此,读取 A::m_nA 和 C::WhoAmI() 时读入的是内存垃圾。

因为 Vee-Table 和虚函数位于 shm_server 共享库内部,恰巧在同一虚拟地址被重新加载,所以,再次引用类型 A * 和 B * 的指针时不会观察到任何问题。

因此,GNU 所采用的 C++ 对象模型没有成功地处理虚拟继承。

例 2:shm_client1 与 shm_client2

在下一个示例输出中,在命令行中首先执行 shm_client1,然后执行 shm_client2:

清单 7. shm_client1 与 shm_client2 的输出日志

$ ./shm_client1

Created the shared memory
1073844224 1073845248 1073846272
1
Object type: A
2
Object type: B
3
Object type: C

$ ipcs

—— Shared Memory Segments ——–
key shmid owner perms bytes nattch status
0×000004d2 2359301 sachin 666 3072 0

$ ./shm_client2
1073942528 1073943552 1073944576
1
-> Segmentation fault (core dumped)

$ ./shm_client2 3
1073942528 1073943552 1073944576
2
-> Segmentation fault (core dumped)

$ ./shm_client2 5
1073942528 1073943552 1073944576
-> 1048594
-> Segmentation fault (core dumped)

然而,Vee-Table 位于 shm_server 共享库内部:在 shm_client1 和 shm_client2 中它被加载到不同的虚地址。这样,读取 A::WhoAmI() 和 B::WhoAmI() 时读入的都是内存垃圾。

运用共享内存

在共享内存中具体使用 C++ 对象时您应该考虑两个主要问题。首先,Vee-Table 指针用于访问虚函数,而对数据成员的访问直接使用编译时偏移量实现。因此,对于所有这种共享对象来说,所有进程中的 Vee-Table 和虚函数都应该具有相同的虚地址。关于这一点,没有一成不变的规则,不过,为相关的共享库采用适当的链接顺序大部分时候都会管用。

另外,永远不要忘记,虚拟继承的对象有指向基对象的基指针。基指针引用进程的数据段,而且永远是特定于进程的。难以确保所有的客户机进程中都有相同的数字值。因此,假如使用 C++ 对象模型,要避免在共享内存中构造虚拟继承的对象。但是,也不要忘记,不同的编译器采用的是不同的对象模型。例如,Microsoft Compiler 使用进程无关的偏移量来为虚拟继承类指定基对象,因而不存在这个问题。重要的是,确保所有客户机进程中的共享对象的地址相同。

参考资料

参考资料

您可以参阅本文在 developerWorks 全球站点上的 英文原文.

Stanley B. Lippman 的 Inside the C++ Object Model (Addison-Wesley, 1996)是理解 C++ 内部机制的极好参考资料。

RFC 1014 – XDR: External Data Representation Standard是描述和编码不同计算机体系结构间的数据的标准。它定义了描述字符串、变长数组以及类似的结构体的语言。

不要忘记去查看关于 ld、ldd、ipcs、ipcrm 等各个函数的系统手册页。

尽管 Eclipse 主要是一个 Java 开发环境,它的体系结构也确保了对其他编程语言的支持。通过 用 Eclipse 平台进行 C/C++ 开发( developerWorks, 2003 年 4 月)一文深入学习。

IBM 的 VisualAge C++是一个高级 C/C++ 编译器,有用于 AIX 和部分 Linux 发行版本的版本。

Java programming for C/C++ developers( developerWorks,2002 年 5 月)教程从已经熟悉 C++ 的开发人员的视角出发介绍了 Java 语言。

在 developerWorks Linux 专区可以找到更多为 Linux 开发者准备的参考资料。

购买 Developer Bookstore Linux 区 打折出售的 Linux 书籍。

通过 developerWorks Subscription 使用最新的 IBM 工具和中间件来开发和测试您的 Linux 应用程序:您可以自 WebSphere、DB2、Lotus、Rational、和 Tivoli 得到 IBM 软件,以及一个可以在 12 个月内使用这些软件的许可,所有的花费都比您想像的要低。

自 developerWorks 的 为您的 Linux 应用开发加油提速 专区下载可以运行于 Linux 之上的经过挑选的 developerWorks Subscription 产品免费测试版本,包括 WebSphere Studio Site Developer、WebSphere SDK for Web services、WebSphere Application Server、DB2 Universal Database Personal Developers Edition、Tivoli Access Manager 和 Lotus Domino Server。要更快速地开始上手,请参阅针对各个产品的 how-to 文章和技术支持。

关于作者
Sachin 五年来一直在从事 C++ 各方面的工作,包括为期三年的对各种编译器的 C++ 对象模型的研究。他当前在 IBM Global Services India 工作。您可以通过 sachin_agrawal@in.ibm.com 与他联系。

对本文的评价
您对这篇文章的看法如何?

太差! (1) 需提高 (2) 一般;尚可 (3) 好文章 (4) 真棒!(5)

建议?

developerWorks 中国 > Linux >

 

胸怀的宽广度是一个人的事业限度

类归于: 闲聊 — hunter @ 2:21 下午

胸怀的宽广度也是一个人的事业限度,如果心中容不下异类,听不进别人的不同意见,不会和敌人共存,很难想像这样的人会取得成功。心胸狭窄的人眼里容不下一粒沙子,不允许别人有一丝一毫的错误,得失心很重,不能批评,这样的人只能活在自己的世界里,不具备成就事业的起码素质。

《史记》有言:“大行不顾细谨,大礼不辞小让”,胸怀宽广的人总是不拘小节,能够随时把眼光控制在大目标上,让小事情为大事情服务。胸怀坦荡者一般具备下面几个特征:

1. 给人视觉印象分明,轮廓突出,自然随意,没有犹豫不定的感觉。额头较高的人一般智商很高,很聪明;额头眉骨凸现的人有一股傲气,不服输,喜欢争斗;额头宽阔给人感觉大气,容易为别人接受,达成一致意见,这种人往往成为一个单位里的领导者,或者是后备的领导干部。

2. 视野的关注点总是集中在自己的理想或者目标上,不会斤斤计较于日常琐事。要成就大事首先需要一个“种子”和环境,种子就是人的理想,环境就是适合种子生长的人的素质,其首要的就是人的胸怀和气度。所谓要取得天下要先有容纳天下的气度,就是这个道理。

3. 具有乐观的人生态度,不会让小事情主导自己的心情。心胸宽广的人往往会对人生抱一种积极向上的看法,很达观,可以娴熟地驾驭自己的心情。

十一月 9, 2005

更新procps package

类归于: Linux — hunter @ 12:26 下午

1. goto rpmfind.org
2. download procps-3.2.3-8.2.src.rpm
3. rpm -i *.rpm
4. cd /usr/src/rpm/SOURCES/
tar zxvf procps-3.2.3.tar.gz
cd procps-3.2.3
vi proc/module.mk
change
SHARED := 1
to
SHARED := 0
5. make
6. 安全起见,你可以拷贝./top -> /usr/bin/top2 ,这样你久有两个不同版本的top :)

十一月 1, 2005

关于系统优化

类归于: 闲聊 — hunter @ 10:59 上午

其实就是几条原则而已:
1. 80/20 , 采集数据,分析瓶颈在哪里,数据也可以作为今后优化结果对比
2. 分离读写,互联网应用,80%都是读,将这些response cache,就可以大大减轻系统负荷
3. 按业务分类,重要的业务服务器重点保护

2005-10-31 16:38:30 某人
总算看见你了

2005-10-31 16:38:31 某人
呵呵

2005-10-31 16:38:39 某人
有空吗

2005-10-31 16:38:10 hunter

2005-10-31 16:38:49 hunter
xxx 跟我说了
(2005-10-31 16:28:43) hunter
你要描述一下现状,和希望达到的目标哦

2005-10-31 16:39:42 某人
ok

2005-10-31 16:38:59 hunter
你们遇到什么问题了?

2005-10-31 16:40:29 某人
是这样的,xxx.xxx.net现在是简单基于apapche+php+mysql这样的服务器,负载不是很均衡

2005-10-31 16:40:46 某人
用户数上来就感觉有点撑不住了

2005-10-31 16:41:04 某人
想请教一下系统架构负载均衡,和优化

2005-10-31 16:41:34 某人
希望能够满足10w/日用户这样的访问量,和下载

2005-10-31 16:41:40 某人
jar

2005-10-31 16:41:49 hunter
现在机器的硬件是什么?有多少台机器?pv多少,带宽吃了多少?

2005-10-31 16:42:40 某人
看看你能不能给我们些建议

2005-10-31 16:42:53 某人
硬件就是dell的服务器一台

2005-10-31 16:43:10 某人
pv多少,带宽吃了多少? 这些没有数据

2005-10-31 16:43:24 某人
但是目前已经接近负荷

2005-10-31 16:43:36 hunter
pv统计都没有啊?grep 一下apache log就可以得到一个初步的了

2005-10-31 16:43:44 hunter
cpu很忙?

2005-10-31 16:44:34 某人
是的

2005-10-31 16:45:17 某人
所以看看你有没有比较好的建议,如何让用户访问达到负载均衡

2005-10-31 16:45:29 某人
服务器,我们还会增加的

2005-10-31 16:46:06 某人
目前主要是怎么让http访问, mysql访问能满足大用户数的

2005-10-31 16:46:26 某人
不知道你可否有更好的建议,或idea

2005-10-31 16:46:09 hunter
做网站负载均衡有很多方法

2005-10-31 16:46:58 某人
是的

2005-10-31 16:46:26 hunter
但是首先要摸底,你什么数据都没有,很难说明优化的效果

2005-10-31 16:47:11 某人
所以想你给个比较好的建议

2005-10-31 16:46:38 hunter
很可能系统优化一下就可以了

2005-10-31 16:46:47 hunter
你能否申请一个免费的计数器?

2005-10-31 16:47:31 某人
不会这样简单瓜

2005-10-31 16:46:53 hunter
放在网站上?

2005-10-31 16:47:03 hunter
嗯,是真的

2005-10-31 16:47:50 某人
暂时没有

2005-10-31 16:48:24 某人
依你的经验,那些做法会比较ok些呢

2005-10-31 16:47:42 hunter
如果系统已经到了极限,再做负载均衡未迟,但是应用的优化往往可以延迟做平行扩展的时间

2005-10-31 16:47:52 hunter
1. 对页面做静态化cache

2005-10-31 16:48:35 某人

2005-10-31 16:48:46 某人
这个有考虑

2005-10-31 16:48:08 hunter
比如你的分类浏览,是一个比较复杂的sql查询,可以考虑cache

2005-10-31 16:48:18 hunter
很多php模板都支持cache的

2005-10-31 16:48:22 hunter
比如smarty

2005-10-31 16:49:14 某人
好的

2005-10-31 16:48:42 hunter
最简单的负载均衡,就是做DNS轮训

2005-10-31 16:49:38 某人

2005-10-31 16:48:59 hunter
两台完全一样的web server,共同访问一个db

2005-10-31 16:49:11 hunter
图片和db存再同一台机器上

2005-10-31 16:49:59 某人
这个也考虑过

2005-10-31 16:49:21 hunter
用nfs mount到这台机器上

2005-10-31 16:49:58 hunter
主要就是分离动态和静态数据

2005-10-31 16:50:44 某人
基本上会考虑apache server和mysql server独立

2005-10-31 16:50:10 hunter
用户80%的访问都是read操作

2005-10-31 16:51:08 某人
ok

2005-10-31 16:50:30 hunter
将这些最耗时的操作,cache起来,或者静态化,就可以大大减轻系统负担了

2005-10-31 16:50:55 hunter
如果你只是分离成两台机器,但是每个PHP还是直连mysql,那么很快你的mysql服务器也会成为瓶颈的

2005-10-31 16:51:56 某人
是的

2005-10-31 16:51:31 hunter
你可以到mysql上面,show full process一下,看看哪些sql出现的频率最高,就专门优化一下这些sql,或者使用这些sql的php

2005-10-31 16:52:29 某人
ok

2005-10-31 16:53:53 hunter
top一下,看看最耗cpu的几个进程是什么?

2005-10-31 16:54:48 某人

2005-10-31 16:55:08 某人
这些我都记下来了

2005-10-31 16:55:20 某人
mysql是其中之一

2005-10-31 16:55:10 hunter
嗯,那要好好分析mysql慢的原因

2005-10-31 16:55:22 hunter
通常就是sql语句或者表结构的问题

2005-10-31 16:55:28 hunter
比如没有加键

2005-10-31 16:55:40 hunter
单表数据量如果太大了,就要考虑分表

2005-10-31 16:55:47 hunter
mysql单表不要超过100w

2005-10-31 16:56:03 hunter
如果性能不行,或者内存不够,还要再少点

2005-10-31 16:56:11 hunter
机器尽量加内存,加到2G

2005-10-31 16:57:06 某人
ok

2005-10-31 16:56:29 hunter
以便让php和mysql充分利用内存做cache

2005-10-31 16:58:04 某人
hunter 16:55:47
mysql单表不要超过100w --这个还没有发生

2005-10-31 16:58:21 hunter
呵呵,其实到了50w你就要开始考虑重新改造程序,以便支持分表了

2005-10-31 16:59:33 某人
这样啊

2005-10-31 17:00:30 某人
你刚刚提到的php访问mysql,这其实是个瓶颈,有没有好的方法

2005-10-31 16:59:48 hunter
嗯,后面的数据滚动一般比较快

2005-10-31 17:00:14 hunter
php有很多cache机制啊?什么zend,smarty之类的

2005-10-31 17:00:20 hunter
我对php不大了解,可能要问问别人

2005-10-31 17:01:12 某人
又用zend

2005-10-31 17:00:34 hunter
其实如果你把大部分数据静态化之后,mysql就不是问题了

2005-10-31 17:00:38 hunter
哦,效果如何?

2005-10-31 17:01:24 某人

2005-10-31 17:01:39 某人
我大概知道了你的思路

2005-10-31 17:01:48 某人
服务器方面呢

2005-10-31 17:02:25 某人
据我知道你负责的是个很大的项目,应该有让你解决的类似问题

2005-10-31 17:02:34 某人
请说说好吗

2005-10-31 17:02:57 某人
现在我们打算重新改造,设计架构

2005-10-31 17:03:13 某人
你的经验可以对我们的一大财富啊

2005-10-31 17:03:14 某人
呵呵

2005-10-31 17:02:59 hunter
呵呵,你不用考虑太复杂

2005-10-31 17:03:48 某人
哦,是吗

2005-10-31 17:03:10 hunter
我看你们的业务也不是很复杂的

2005-10-31 17:03:57 某人
是的

2005-10-31 17:03:19 hunter
但是数据收集还是必要的

2005-10-31 17:04:08 某人
其实就是下载站

2005-10-31 17:03:28 hunter
做系统优化的时候要抓住80/20原则

2005-10-31 17:04:16 某人
ok

2005-10-31 17:03:37 hunter
重点优化最耗时的地方

2005-10-31 17:04:26 某人

2005-10-31 17:03:52 hunter
你可以用一些工具分析一下apache log,看看哪些地方访问的比较多

2005-10-31 17:04:42 某人
ok

2005-10-31 17:04:09 hunter
apache处理静态数据的话,可以支持到100Mbps/机器

2005-10-31 17:05:03 某人
哦,这样

2005-10-31 17:04:52 hunter
建议你们首先把浏览的部分和操作(写)的部分分离

2005-10-31 17:05:06 hunter
比如list, detail,都可以单独1-2台机器

2005-10-31 17:05:15 hunter
然后把业务处理部分单独一台出来

2005-10-31 17:05:30 hunter
社区和其他不重要的业务再单独一台

2005-10-31 17:06:26 某人

2005-10-31 17:05:44 hunter
db可以用好一些的机器

2005-10-31 17:05:57 hunter
mysql可以支持到 300-400个并发链接

2005-10-31 17:06:43 某人
但是用户数增长很快,这个有什么好的建议吗

2005-10-31 17:06:01 hunter
内存要大

2005-10-31 17:06:20 hunter
主要看活跃用户数量咯

2005-10-31 17:06:25 hunter
还有多少的写请求

2005-10-31 17:06:38 hunter
下载的服务器独立出来

2005-10-31 17:07:29 某人
ok

2005-10-31 17:06:56 hunter
建议用一些轻量级的web server,比如thttpd来替代apache作为下载服务器

2005-10-31 17:07:54 某人
我们这边的用户有时候是在比较集中的一段时间访问

2005-10-31 17:07:14 hunter
你运行 mysqladmin status

2005-10-31 17:07:23 hunter
看看db的平均query数量?

2005-10-31 17:08:14 某人
ok

2005-10-31 17:08:24 某人
今天不是繁忙时间

2005-10-31 17:08:25 某人
呵呵

2005-10-31 17:07:50 hunter
没关系的,静态数据分离之后,就不成问题的了

2005-10-31 17:08:01 hunter
把读写php分离到不同机器上

2005-10-31 17:08:41 某人
节假日

2005-10-31 17:08:11 hunter
读的给多些机器,写的少一点

2005-10-31 17:09:02 某人
哦可

2005-10-31 17:09:04 某人
ok

2005-10-31 17:08:31 hunter
把重要的业务DB跟一般的社区类的DB分离开

2005-10-31 17:09:26 某人
好的

2005-10-31 17:09:22 hunter
ok,大概的优化逻辑就是这样

2005-10-31 17:11:28 某人
如果你能考虑到更多的地方,请告诉我吧,

2005-10-31 17:11:44 某人
真的很谢谢你给了这么多建议

2005-10-31 17:11:50 某人
thanks a lot

2005-10-31 17:11:27 hunter
:)

2005-10-31 17:12:22 某人
3F801A

WordPress 所驱动