如何架构一个新的综合金融交易系统

 

技术产品需求

客户端网站系统(app),业务系统(接口),运维平台

网站系统:

基础系统:

1.用户(帐户)管理系统 2.商品(行情)管理系统 3.订单(交易管理)系统 4.支付系统5.风控系统

增值系统:

客户服务系统,筛选筛选系统,资讯系统。。。

业务系统:

管理系统:

用户管理系统

运营支撑系统

核心系统:

订单交易系统

查询系统

行情系统

增值系统:

数据统计(报表)系统

客户服务系统

 

技术栈选型

1.前端系统

2.后端系统

2.1 负载均衡及http 服务器接入:nginx

2.2 web 应用容器 SOA 服务 Tomcat + dubbo

2.3 文件服务器

        2.3.1 储存方式
        2.3.2 储存容量
        2.3.3 安全性与存取权限控管
        2.3.4 存取效能

2.4 缓存服务器
         2.4.1 分布式Redis缓存
         2.4.2 Memcache缓存

2.5 消息系统
         2.5.1 ActiveMQ
         2.5.2 分布式消息系统Kafka、Rocketmq等

2.6 数据持久层
         2.6.1 关系型数据库
              (1). Mysql

        2.6.2 Nosql
              (1). MongoDB

Linux IO模式及 select、poll、epoll详解(转载)

这是目前入门级别感觉比较清晰又详尽的IO模式讲解,值得一看。

注:本文是对众多博客的学习和总结,可能存在理解错误。请带着怀疑的眼光,同时如果有错误希望能指出。

同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的。所以先限定一下本文的上下文。

本文讨论的背景是Linux环境下的network IO。

一 概念说明

在进行解释之前,首先要说明几个概念:
– 用户空间和内核空间
– 进程切换
– 进程的阻塞
– 文件描述符
– 缓存 I/O

用户空间与内核空间

现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方)。操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操心系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间。针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空间,而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF),供各个进程使用,称为用户空间。

进程切换

为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说,任何进程都是在操作系统内核的支持下运行的,是与内核紧密相关的。

从一个进程的运行转到另一个进程上运行,这个过程中经过下面这些变化:
1. 保存处理机上下文,包括程序计数器和其他寄存器。
2. 更新PCB信息。
3. 把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
4. 选择另一个进程执行,并更新其PCB。
5. 更新内存管理的数据结构。
6. 恢复处理机上下文。

注:总而言之就是很耗资源,具体的可以参考这篇文章:进程切换

进程的阻塞

正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才可能将其转为阻塞状态。当进程进入阻塞状态,是不占用CPU资源的

文件描述符fd

文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

缓存 I/O

缓存 I/O 又被称作标准 I/O,大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中,操作系统会将 I/O 的数据缓存在文件系统的页缓存( page cache )中,也就是说,数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。

缓存 I/O 的缺点:
数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作,这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。

二 IO模式

刚才说了,对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个read操作发生时,它会经历两个阶段:
1. 等待数据准备 (Waiting for the data to be ready)
2. 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

正式因为这两个阶段,linux系统产生了下面五种网络模式的方案。

同步:
– 阻塞 I/O(blocking IO)
– 非阻塞 I/O(nonblocking IO)
– I/O 多路复用( IO multiplexing)
– 信号驱动 I/O( signal driven IO)

异步:
– 异步 I/O(asynchronous IO)

注:由于signal driven IO在实际中并不常用,所以我这只提及剩下的四种IO Model。

阻塞 I/O(blocking IO)

在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:

当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据(对于网络IO来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的UDP包。这个时候kernel就要等待足够的数据到来)。这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)。当kernel一直等到数据准备好了,它就会将数据从kernel中拷贝到用户内存,然后kernel返回结果,用户进程才解除block的状态,重新运行起来。

所以,blocking IO的特点就是在IO执行的两个阶段都被block了。

非阻塞 I/O(nonblocking IO)

linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:

当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。

所以,nonblocking IO的特点是用户进程需要不断的主动询问kernel数据好了没有。

I/O 多路复用( IO multiplexing)

IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO。select/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。

当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回。

这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。

所以,如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)

在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是,如上图所示,整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO给block。

信号驱动式U/O模型:

         可以用信号,让内核在描述符就绪时发送SIGIO信号通知我们。称为信号驱动式I/O

        我们首先开启套接字的信号驱动式I/O功能,并通过sigaction系统调用安装一个信号处理函数。该系统调用将立即返回,我们的进程继续工作,也就是说它没有被阻塞。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。我们随后既可以在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已准备好待处理。也可以立即通知循环,让它读取数据报。

         无论如何处理SIGIO信号,这种模型的优势在于等待数据报到达期间进程不被阻塞。主循环可以继续执行,只要等待来自信号处理函数的通知:既可以是数据已准备好被处理,也可以是数据报已准备好被读取。

异步 I/O(asynchronous IO)

inux下的asynchronous IO其实用得很少。先看一下它的流程:

用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

总结

blocking和non-blocking的区别

调用blocking IO会一直block住对应的进程直到操作完成,而non-blocking IO在kernel还准备数据的情况下会立刻返回。

synchronous IO和asynchronous IO的区别

在说明synchronous IO和asynchronous IO的区别之前,需要先给出两者的定义。POSIX的定义是这样子的:
– A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;
– An asynchronous I/O operation does not cause the requesting process to be blocked;

两者的区别就在于synchronous IO做”IO operation”的时候会将process阻塞。按照这个定义,之前所述的blocking IO,non-blocking IO,IO multiplexing都属于synchronous IO。

有人会说,non-blocking IO并没有被block啊。这里有个非常“狡猾”的地方,定义中所指的”IO operation”是指真实的IO操作,就是例子中的recvfrom这个system call。non-blocking IO在执行recvfrom这个system call的时候,如果kernel的数据没有准备好,这时候不会block进程。但是,当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了,在这段时间内,进程是被block的。

而asynchronous IO则不一样,当进程发起IO 操作之后,就直接返回再也不理睬了,直到kernel发送一个信号,告诉进程说IO完成。在这整个过程中,进程完全没有被block。

各个IO Model的比较如图所示:

通过上面的图片,可以发现non-blocking IO和asynchronous IO的区别还是很明显的。在non-blocking IO中,虽然进程大部分时间都不会被block,但是它仍然要求进程去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存。而asynchronous IO则完全不同。它就像是用户进程将整个IO操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据。

三 I/O 多路复用之select、poll、epoll详解

select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O的实现会负责把数据从内核拷贝到用户空间。(这里啰嗦下)

select

int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但 是这样也会造成效率的降低。

poll

int poll (struct pollfd *fds, unsigned int nfds, int timeout);

不同与select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。

struct pollfd { int fd; /* file descriptor */ short events; /* requested events to watch */ short revents; /* returned events witnessed */ };

pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。同时,pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。

从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

epoll

epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。

一 epoll操作过程

epoll操作过程需要三个接口,分别如下:

int epoll_create(int size);//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

1. int epoll_create(int size);
创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大,这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值,参数size并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议
当创建好epoll句柄后,它就会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
函数是对指定描述符fd执行op操作。
– epfd:是epoll_create()的返回值。
– op:表示op操作,用三个宏来表示:添加EPOLL_CTL_ADD,删除EPOLL_CTL_DEL,修改EPOLL_CTL_MOD。分别添加、删除和修改对fd的监听事件。
– fd:是需要监听的fd(文件描述符)
– epoll_event:是告诉内核需要监听什么事,struct epoll_event结构如下:

struct epoll_event { __uint32_t events; /* Epoll events */ epoll_data_t data; /* User data variable */ }; //events可以是以下几个宏的集合: EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭); EPOLLOUT:表示对应的文件描述符可以写; EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来); EPOLLERR:表示对应的文件描述符发生错误; EPOLLHUP:表示对应的文件描述符被挂断; EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。 EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
等待epfd上的io事件,最多返回maxevents个事件。
参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

二 工作模式

epoll对文件描述符的操作有两种模式:LT(level trigger)ET(edge trigger)。LT模式是默认模式,LT模式与ET模式的区别如下:
LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。

1. LT模式

LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的。

2. ET模式

ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)

ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

3. 总结

假如有这样一个例子:
1. 我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符
2. 这个时候从管道的另一端被写入了2KB的数据
3. 调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作
4. 然后我们读取了1KB的数据
5. 调用epoll_wait(2)……

LT模式:
如果是LT模式,那么在第5步调用epoll_wait(2)之后,仍然能受到通知。

ET模式:
如果我们在第1步将RFD添加到epoll描述符的时候使用了EPOLLET标志,那么在第5步调用epoll_wait(2)之后将有可能会挂起,因为剩余的数据还存在于文件的输入缓冲区内,而且数据发出端还在等待一个针对已经发出数据的反馈信息。只有在监视的文件句柄上发生了某个事件的时候 ET 工作模式才会汇报事件。因此在第5步的时候,调用者可能会放弃等待仍在存在于文件输入缓冲区内的剩余数据。

当使用epoll的ET模型来工作时,当产生了一个EPOLLIN事件后,
读数据的时候需要考虑的是当recv()返回的大小如果等于请求的大小,那么很有可能是缓冲区还有数据未读完,也意味着该次事件还没有处理完,所以还需要再次读取:

while(rs){ buflen = recv(activeevents[i].data.fd, buf, sizeof(buf), 0); if(buflen < 0){ // 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读 // 在这里就当作是该次事件已处理处. if(errno == EAGAIN){ break; } else{ return; } } else if(buflen == 0){ // 这里表示对端的socket已正常关闭. } if(buflen == sizeof(buf){ rs = 1; // 需要再次读取 } else{ rs = 0; } }

Linux中的EAGAIN含义

Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。
从字面上来看,是提示再试一次。这个错误经常出现在当应用程序进行一些非阻塞(non-blocking)操作(对文件或socket)的时候。

例如,以 O_NONBLOCK的标志打开文件/socket/FIFO,如果你连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返回,read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试。
又例如,当一个系统调用(比如fork)因为没有足够的资源(比如虚拟内存)而执行失败,返回EAGAIN提示其再调用一次(也许下次就能成功)。

三 代码演示

下面是一段不完整的代码且格式不对,意在表述上面的过程,去掉了一些模板代码。

#define IPADDRESS "127.0.0.1" #define PORT 8787 #define MAXSIZE 1024 #define LISTENQ 5 #define FDSIZE 1000 #define EPOLLEVENTS 100 listenfd = socket_bind(IPADDRESS,PORT); struct epoll_event events[EPOLLEVENTS]; //创建一个描述符 epollfd = epoll_create(FDSIZE); //添加监听描述符事件 add_event(epollfd,listenfd,EPOLLIN); //循环等待 for ( ; ; ){ //该函数返回已经准备好的描述符事件数目 ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1); //处理接收到的连接 handle_events(epollfd,events,ret,listenfd,buf); } //事件处理函数 static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf) { int i; int fd; //进行遍历;这里只要遍历已经准备好的io事件。num并不是当初epoll_create时的FDSIZE。 for (i = 0;i < num;i++) { fd = events[i].data.fd; //根据描述符的类型和事件类型进行处理 if ((fd == listenfd) &&(events[i].events & EPOLLIN)) handle_accpet(epollfd,listenfd); else if (events[i].events & EPOLLIN) do_read(epollfd,fd,buf); else if (events[i].events & EPOLLOUT) do_write(epollfd,fd,buf); } } //添加事件 static void add_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev); } //处理接收到的连接 static void handle_accpet(int epollfd,int listenfd){ int clifd; struct sockaddr_in cliaddr; socklen_t cliaddrlen; clifd = accept(listenfd,(struct sockaddr*)&cliaddr,&cliaddrlen); if (clifd == -1) perror("accpet error:"); else { printf("accept a new client: %s:%d\n",inet_ntoa(cliaddr.sin_addr),cliaddr.sin_port); //添加一个客户描述符和事件 add_event(epollfd,clifd,EPOLLIN); } } //读处理 static void do_read(int epollfd,int fd,char *buf){ int nread; nread = read(fd,buf,MAXSIZE); if (nread == -1) { perror("read error:"); close(fd); //记住close fd delete_event(epollfd,fd,EPOLLIN); //删除监听 } else if (nread == 0) { fprintf(stderr,"client close.\n"); close(fd); //记住close fd delete_event(epollfd,fd,EPOLLIN); //删除监听 } else { printf("read message is : %s",buf); //修改描述符对应的事件,由读改为写 modify_event(epollfd,fd,EPOLLOUT); } } //写处理 static void do_write(int epollfd,int fd,char *buf) { int nwrite; nwrite = write(fd,buf,strlen(buf)); if (nwrite == -1){ perror("write error:"); close(fd); //记住close fd delete_event(epollfd,fd,EPOLLOUT); //删除监听 }else{ modify_event(epollfd,fd,EPOLLIN); } memset(buf,0,MAXSIZE); } //删除事件 static void delete_event(int epollfd,int fd,int state) { struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev); } //修改事件 static void modify_event(int epollfd,int fd,int state){ struct epoll_event ev; ev.events = state; ev.data.fd = fd; epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev); } //注:另外一端我就省了

四 epoll总结

在 select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一 个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait() 时便得到通知。(此处去掉了遍历文件描述符,而是通过监听回调的的机制。这正是epoll的魅力所在。)

epoll的优点主要是一下几个方面:
1. 监视的描述符数量不受限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左 右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。select的最大缺点就是进程打开的fd是有数量限制的。这对 于连接数量比较大的服务器来说根本不能满足。虽然也可以选择多进程的解决方案( Apache就是这样实现的),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。

  1. IO的效率不会随着监视fd的数量的增长而下降。epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数。

如果没有大量的idle -connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当遇到大量的idle- connection,就会发现epoll的效率大大高于select/poll。

Java nio和多路复用

java 1.4 nio提供的select,这是一种多路复用I/O(multiplexed non-blocking I/O)模型,底层是使用select或者poll。I/O复用就是,阻塞在select或者poll系统调用的某一个之上,而不是阻塞在真正的I/O系统调用之上。JDK 5.0 update 9和JDK 6.0在linux下支持使用epoll,可以提高并发idle connection的性能(http://blogs.sun.com/alanb/entry/epoll)。
“BIO是指阻塞IO方式,即读和写必须为同步方式,NIO是指异步(用户进程未被IO阻塞)读,同步(用户进程被IO阻塞)写的方式,AIO是指异步读,异步写的方式。
在网络协议上java对于TCP/IP和UDP/IP均支持,在网络IO的操作上,目前java仅支持BIO和NIO两种方式。”

(读:读取内核状态是否准备好写:数据内核态->用户态的拷贝

Reactor 和 Proactor

这是目前网络编程中经常用到两种事件驱动的设计模式。

其实结合上面来看,reactor 模式思想就是基于非阻塞同步IO的方式,也即I/O 多路复用。

其流程如下:

  • 步骤 1) 等待事件 (Reactor 的工作)
  • 步骤 2) 发”已经可读”事件发给事先注册的事件处理者或者回调 ( Reactor 要做的)
  • 步骤 3) 读数据 (用户代码要做的)
  • 步骤 4) 处理数据 (用户代码要做的)

而proactor模式思想基于完全异步IO方式。

  • 步骤 1) 等待事件 (Proactor 的工作)
  • 步骤 2) 读数据(看,这里变成成了让 Proactor 做这个事情)
  • 步骤 3) 把数据已经准备好的消息给用户处理函数,即事件处理者(Proactor 要做的)
  • 步骤 4) 处理数据 (用户代码要做的)

区别就在第三步骤,所以如果我们的网络库使用了Reactor设计模式,使用的是IO复用,那么可以通过封装第三个步骤,变成proactor 模式,这样可以让用户进程对读数据这一步无感知,那么使用了 Proactor设计模式封装过的IO多路复用模型在使用上与异步IO模型无差异,为什么这样做,虽然不会提升我们系统的性能,我们的服务在跨平台的时候可以让用户代码脱离 IO模型可能不一样的限制,降低耦合,提高生产率。

参考

用户空间与内核空间,进程上下文与中断上下文[总结]
进程切换
维基百科-文件描述符
Linux 中直接 I/O 机制的介绍
IO – 同步,异步,阻塞,非阻塞 (亡羊补牢篇)
Linux中select poll和epoll的区别
IO多路复用之select总结
IO多路复用之poll总结
IO多路复用之epoll总结

(转载自:https://segmentfault.com/a/1190000003063859,作者:人云思云

FTP服务器及账号访问设置

背景

最近做个项目与其他系统对接,接口数据他们非要以文件格式获取,好吧,

按照他们的需求要弄个FTP服务器,然后他们的系统还卖了好多地方(好有钱),

然后他们都要来取数据,有些数据公用的,有些数据还是私有的,好吧还要整好多

账号起来!

安装

FTP服务器是部署在linux系统上的,所以很自然就想到了用vsftpd,其他都不多说了,

直接把FTP服务安装起来再说了!安装还是直接使用yum(真神器!):

yum install vsftpd

就这么简单,然后啥都装好了!然后就启动服务就可以了:

service vsftpd start

配置

vsftpd.conf

vsftpd.conf 是vsftpd的配置文件,用来控制vsftpd的各项功能。默认状态下,它的位置

是/etc/vsftpd.conf或者在/etc/vsftpd/vsftpd.conf。里面的配置项的含义还是需要了解一番,

这里我就不多做介绍,你们直接传送门走起!

虚拟用户

看完上面的配置,那么我们就来进行本次重点内容虚拟用户的配置过程:

基本思路就是先创建一个用户,然后在这个用户下面挂多个虚拟的用户

进行!

mysql5.6安装

RPM方式安装MySQL5.6

a. 检查MySQL及相关RPM包,是否安装,如果有安装,则移除(rpm –e 名称)

1
[root@localhost ~]# rpm -qa | grep -i mysql

2
mysql-libs-5.1.66-2.el6_3.x86_64

3
[root@localhost ~]# yum -y remove mysql-libs*

b. 下载Linux对应的RPM包,如:CentOS6.4_64对应的RPM包,如下:

1
[root@localhost rpm]# ll

2
total 74364

3
-rw-r--r--. 1 root root 18442536 Dec 11 20:19 MySQL-client-5.6.15-1.el6.x86_64.rpm

4
-rw-r--r--. 1 root root  3340660 Dec 11 20:06 MySQL-devel-5.6.15-1.el6.x86_64.rpm

5
-rw-r--r--. 1 root root 54360600 Dec 11 20:03 MySQL-server-5.6.15-1.el6.x86_64.rpm

c. 安装MySQL

1
[root@localhost rpm]# rpm -ivh MySQL-server-5.6.15-1.el6.x86_64.rpm

2
[root@localhost rpm]# rpm -ivh MySQL-devel-5.6.15-1.el6.x86_64.rpm

3
[root@localhost rpm]# rpm -ivh MySQL-client-5.6.15-1.el6.x86_64.rpm

4
#修改配置文件位置

5
[root@localhost rpm]# cp /usr/share/mysql/my-default.cnf /etc/my.cnf

d. 初始化MySQL及设置密码

1
[root@localhost rpm]# /usr/bin/mysql_install_db

2
[root@localhost rpm]# service mysql start

3
[root@localhost rpm]# cat /root/.mysql_secret  #查看root账号密码

4
# The random password set for the root user at Wed Dec 11 23:32:50 2013 (local time): qKTaFZnl

5
[root@localhost ~]# mysql -uroot –pqKTaFZnl

6
mysql> SET PASSWORD = PASSWORD('123456');    #设置密码为123456

7
mysql> exit

8
[root@localhost ~]# mysql -uroot -p123456

e. 允许远程登陆

01
mysql> use mysql;

02
mysql> select host,user,password from user;

03
+-----------------------+------+-------------------------------------------+

04
| host                  | user | password                                  |

05
+-----------------------+------+-------------------------------------------+

06
| localhost             | root | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |

07
| localhost.localdomain | root | *1237E2CE819C427B0D8174456DD83C47480D37E8 |

08
| 127.0.0.1             | root | *1237E2CE819C427B0D8174456DD83C47480D37E8 |

09
| ::1                   | root | *1237E2CE819C427B0D8174456DD83C47480D37E8 |

10
+-----------------------+------+-------------------------------------------+

11

12
mysql> update user set password=password('123456') where user='root';

13
mysql> update user set host='%' where user='root' and host='localhost';

14
mysql> flush privileges;

15
mysql> exit

f. 设置开机自启动

1
[root@localhost ~]# chkconfig mysql on

2
[root@localhost ~]# chkconfig --list | grep mysql

3
mysql           0:off   1:off   2:on    3:on    4:on    5:on    6:off

g. MySQL的默认安装位置

1
/var/lib/mysql/               #数据库目录

2
/usr/share/mysql              #配置文件目录

3
/usr/bin                     #相关命令目录

4
/etc/init.d/mysql              #启动脚本

修改字符集和数据存储路径

配置/etc/my.cnf文件,修改数据存放路径、mysql.sock路径以及默认编码utf-8.

view plain copy

在CODE上查看代码片派生到我的代码片

  1. [client]  
  2. password        = 123456  
  3. port            = 3306  
  4. default-character-set=utf8
  5. [mysqld]  
  6. port            = 3306  
  7. character_set_server=utf8
  8. character_set_client=utf8
  9. collation-server=utf8_general_ci
  10. #(注意linux下mysql安装完后是默认:表名区分大小写,列名不区分大小写; 0:区分大小写,1:不区分大小写)  
  11. lower_case_table_names=1
  12. #(设置最大连接数,默认为 151,MySQL服务器允许的最大连接数16384; )  
  13. max_connections=1000
  14. [mysql]  
  15. default-character-set = utf8

查看字符集

Memcache初探(二)分布式集群

要探讨memcache 的集群,那么就要先理解其特征,伪分布式。

这个要作两层理解:1.服务端数据可以进行分布式部署,但是需要依靠客户端的将数据根据算法进行分布式存储。

2.伪,上面的分布式需要依靠客户端实现,这是它第一个伪,还一个是 memcache 本身做不到可靠性,因为服务端相互之间不通信,不能同步数据,所以如果某一点挂了,就该缓存就失效了,并且如果客户端的算法是直接HASH的话,可能还会 影响后续缓存的命中。

magent

如果让客户端去实现分布式,我觉得这个会麻烦,同时而magent 这个代理就解决了memcashe上述的不足,客户端就只需要存入缓存,取缓存就够了,把一切什么麻烦的分布式,数据同步,可靠性,可用性全都交给magent 。所以一般我们需要集群方式使用memcache 的时候,一般都会增加magent 这个代理节点。当然客户端是少不了,既然提到了那也就讲一下,一般的使用memcache 的流程:

客户端(我们的应用服务) 通过memcache 提供的客户端(SDK)与memcache服务端连接,进行缓存数据的读写操作,如果需要集群,那么我们一般连接到magent,通过magent进行缓存数据读写,具体读写到哪个memcache节点,magent替我们做主,可靠性,可用性等也通过magent帮我们解决,其分布式使用.

magent wiki 给我们的简单示意图,这样memcached 就有主备,可以解决单点问题。

magent is a proxy server sitting between memcache clients(such as php programs) and memcached servers. magent have several advantages over direct connections

                Client
                  |
                Magent
                  |
    |--|--|-------------------|--|--|
    |  |  |                   |  |  |
  Normal Servers Farm      Backup Servers Farm
magent安装

1.mkdir magent 
2.cd magent/ 
3.wget http://memagent.googlecode.com/files/magent-0.5.tar.gz
4.tar zxvf magent-0.5.tar.gz 
5./sbin/ldconfig 
6.sed -i “s#LIBS = -levent#LIBS = -levent -lm#g” Makefile 
7.make 
8.cp magent /usr/bin/magent 
9.cd ../

一步步来,当然安装还是要有root 权限,有啥问题就google 吧,貌似ketama.h

的头文件需要添加以下预定义宏

#ifndef SSIZE_MAX  
#define SSIZE_MAX      32767  
#endif

算上前面一篇这样子我们就安装好了magent,memcached。我们就来个最简单的集群吧:

1.启动memcashe与magent

memcached -m 1 -u weekcashe -d -l 127.0.0.1 -p 11222

memcached -m 1 -u weekcashe -d -l 127.0.0.1 -p 11223


memcached -m 1 -u weekcashe -d -l 127.0.0.1 -p 11224
magent -u weekcashe -n 51200 -l 127.0.0.1 -p 10000 -s 127.0.0.1:11222 -s 127.0.0.1:11223 -b 127.0.0.1:11224

magent -u weekcashe -n 51200 -l 127.0.0.1 -p 11000 -s 127.0.0.1:11222 -s 127.0.0.1:11223 -b 127.0.0.1:11224

2.可用性测试

[weekcashe@iZ28gxqlqfsZ ~]$ telnet 127.0.0.1 10000 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. set key 0 0 8 888888 STORED quit Connection closed by foreign host. [weekcashe@iZ28gxqlqfsZ ~]$ telnet 127.0.0.1 11222 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. get key VALUE key 0 8 888888 END quit Connection closed by foreign host. [weekcashe@iZ28gxqlqfsZ ~]$ telnet 127.0.0.1 11223 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. get key END quit Connection closed by foreign host. [weekcashe@iZ28gxqlqfsZ ~]$ telnet 127.0.0.1 11224 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. get key VALUE key 0 8 888888 END quit Connection closed by foreign host. [weekcashe@iZ28gxqlqfsZ ~]$ telnet 127.0.0.1 11000 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. get key VALUE key 0 8 888888 END quit Connection closed by foreign host.

3.可靠性测试

[weekcashe@iZ28gxqlqfsZ ~]$ ps x
  PID TTY      STAT   TIME COMMAND
 1600 ?        Ssl    0:00 memcached -d -u weekcashe -p 11222 -c 256 -P /tmp/memcached.pid
 1615 ?        Ssl    0:00 memcached -d -u weekcashe -p 11223 -c 256 -P /tmp/memcached.pid
 1630 ?        Ssl    0:00 memcached -d -u weekcashe -p 11224 -c 256 -P /tmp/memcached.pid
 1658 ?        Ss     0:00 magent -u weekcashe -n 51200 -l 127.0.0.1 -p 10000 -s 127.0.0.1:11222 -s 127.0.0.1:11223 -b 127.0.0.1:11224
 1660 ?        Ss     0:00 magent -u weekcashe -n 51200 -l 127.0.0.1 -p 11000 -s 127.0.0.1:11222 -s 127.0.0.1:11223 -b 127.0.0.1:11224
 1726 pts/2    R+     0:00 ps x
[weekcashe@iZ28gxqlqfsZ ~]$ kill 1600
[weekcashe@iZ28gxqlqfsZ ~]$ telnet 127.0.0.1 11222
Trying 127.0.0.1...
telnet: connect to address 127.0.0.1: Connection refused

[weekcashe@iZ28gxqlqfsZ ~]$ telnet 127.0.0.1 10000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get key
VALUE key 0 8

888888
END
quit
Connection closed by foreign host.

[weekcashe@iZ28gxqlqfsZ ~]$ memcached -d -u weekcashe -p 11222 -c 256 -P /tmp/memcached.pid
[weekcashe@iZ28gxqlqfsZ ~]$ ps x
  PID TTY      STAT   TIME COMMAND
 1365 ?        S      0:00 sshd: weekcashe@pts/0
 1366 pts/0    Ss     0:00 -bash
 1487 ?        S      0:00 sshd: weekcashe@pts/2
 1488 pts/2    Ss     0:00 -bash
 1615 ?        Ssl    0:00 memcached -d -u weekcashe -p 11223 -c 256 -P /tmp/memcached.pid
 1630 ?        Ssl    0:00 memcached -d -u weekcashe -p 11224 -c 256 -P /tmp/memcached.pid
 1658 ?        Ss     0:00 magent -u weekcashe -n 51200 -l 127.0.0.1 -p 10000 -s 127.0.0.1:11222 -s 127.0.0.1:11223 -b 127.0.0.1:11224
 1660 ?        Ss     0:00 magent -u weekcashe -n 51200 -l 127.0.0.1 -p 11000 -s 127.0.0.1:11222 -s 127.0.0.1:11223 -b 127.0.0.1:11224
 1730 ?        Ssl    0:00 memcached -d -u weekcashe -p 11222 -c 256 -P /tmp/memcached.pid
 1744 pts/2    R+     0:00 ps x

[weekcashe@iZ28gxqlqfsZ ~]$ telnet 127.0.0.1 10000
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
get key
END

我可以看出,当11222挂掉的时候,magent依然可以从备机器中取到缓存数据。

但是当11222被重新启动后,因为数据丢失,但是magent 未更新hash 对,依然命中到 11222去取数据,导致取不到数据。备机数据失效。这个问题留待解决。

你好通过 magent 端口 10000 写入的数据存储在了 memcashe 端口11222及备份memcashe 11224上,并且可以在 magent 11000 上获取到。并且设置获取数据magent 与直接在memcashe 端口上的接口是一致的。

OK,现在后台模拟集群的memcashe环境已经搭建完毕,通过magent 也已经解决了伪分布式带来的一些麻烦,那么接下就可以直接开始使用用客户端接口进行实际编码工作了。

xmemcached 客户端SDK使用了 NIO 机制,可以方便实现大并发。如果一般玩票性质就直接上 memcached client for java ,虽然其通信方式是传统的阻塞IO模型但足够稳定,不是超大型的已经够用。

Memcache初探-安装

作为一种NOSQL内存式数据库,一般用作数据缓存,目前我们公司用来作为关系型数据库的内存的缓存库,当然做过一些封装与更改,难免在使用上都与原库有所差异,所以就安装下原库试下最原始的用法

既然提了就说一下其特性:伪分布式

 

服务端安装

安装
yum -y install memcached
授权

如果你的安装用户没有root授权,那么刚才的安装会提示错误,需要以下命令授权当前用户,安装不会出错。

su
帮助

使用如下蓝色命令可以查看memcached的使用帮助,参数的作用并附带校验安装是否成功。

# memcached -h
memcached 1.4.4
-p <num>      TCP port number to listen on (default: 11211)
-U <num>      UDP port number to listen on (default: 11211, 0 is off)
-s <file>     UNIX socket path to listen on (disables network support)
-a <mask>     access mask for UNIX socket, in octal (default: 0700)
-l <ip_addr>  interface to listen on (default: INADDR_ANY, all addresses)
-d            run as a daemon
-r            maximize core file limit
-u <username> assume identity of <username> (only when run as root)
-m <num>      max memory to use for items in megabytes (default: 64 MB)
-M            return error on memory exhausted (rather than removing items)
-c <num>      max simultaneous connections (default: 1024)
-k            lock down all paged memory.  Note that there is a
              limit on how much memory you may lock.  Trying to
              allocate more than that would fail, so be sure you
              set the limit correctly for the user you started
              the daemon with (not for -u <username> user;
              under sh this is done with 'ulimit -S -l NUM_KB').
-v            verbose (print errors/warnings while in event loop)
-vv           very verbose (also print client commands/reponses)
-vvv          extremely verbose (also print internal state transitions)
-h            print this help and exit
-i            print memcached and libevent license
-P <file>     save PID in <file>, only used with -d option
-f <factor>   chunk size growth factor (default: 1.25)
-n <bytes>    minimum space allocated for key+value+flags (default: 48)
-L            Try to use large memory pages (if available). Increasing
              the memory page size could reduce the number of TLB misses
              and improve the performance. In order to get large pages
              from the OS, memcached will allocate the total item-cache
              in one large chunk.
-D <char>     Use <char> as the delimiter between key prefixes and IDs.
              This is used for per-prefix stats reporting. The default is
              ":" (colon). If this option is specified, stats collection
              is turned on automatically; if not, then it may be turned on
              by sending the "stats detail on" command to the server.
-t <num>      number of threads to use (default: 4)
-R            Maximum number of requests per event, limits the number of
              requests process for a given connection to prevent 
              starvation (default: 20)
-C            Disable use of CAS
-b            Set the backlog queue limit (default: 1024)
-B            Binding protocol - one of ascii, binary, or auto (default)
-I            Override the size of each slab page. Adjusts max item size
              (default: 1mb, min: 1k, max: 128m)
启动

根据上面的帮助信息结合自己的实际情况启动memcashed服务,-d表示守护进程方式启动。

# memcached -d -u username -c 256 -P /tmp/memcached.pid

默认方式启动

/etc/init.d/memcached start
开机启动
chkconfig --level 2345 memcached on
检测

启动完成后,检测下memcached 服务是否已经OK,由于我们启动使用了默认端口,查看默认配置可以到如下路劲文件去看

vi /etc/sysconfig/memcached

PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS=""

我们看到默认端口是11211,那么我们使用memcashe-tool工具去查看下当前服务状态:

memcached-tool 127.0.0.1:11211 stats
#127.0.0.1:11211   Field       Value
         accepting_conns           1
               auth_cmds           0
             auth_errors           0
                   bytes           0
              bytes_read           7
           bytes_written           0
              cas_badval           0
                cas_hits           0
              cas_misses           0
               cmd_flush           0
                 cmd_get           0
                 cmd_set           0
             conn_yields           0
   connection_structures           6
        curr_connections           5
              curr_items           0
               decr_hits           0
             decr_misses           0
             delete_hits           0
           delete_misses           0
               evictions           0
                get_hits           0
              get_misses           0
               incr_hits           0
             incr_misses           0
          limit_maxbytes    67108864
     listen_disabled_num           0
                     pid        6322
            pointer_size          64
           rusage_system    0.000000
             rusage_user    0.000999
                 threads           4
                    time  1453353845
       total_connections           6
             total_items           0
                  uptime          19
                 version       1.4.4
测试示例
telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
set foo 0 0 3
bar
STORED
get foo
VALUE foo 0 3
bar
END
使用示例
Cache Results

function get_foo(foo_id)
    foo = memcached_get("foo:" . foo_id)
    return foo if defined foo

    foo = fetch_foo_from_database(foo_id)
    memcached_set("foo:" . foo_id, foo)
    return foo
end
关闭
kill `cat /tmp/memcached.pid`
/etc/init.d/memcached stop

 

安装其实还涉及到一个分布式集群安装,因为还涉及到具体应用就放下一章节讲吧!

如何提高服务器并发处理能力

转发,看到的一篇总结得比较平实的博文。

August 16, 2014

说明

以下内容为入门级介绍,意在对老技术作较全的总结而不是较深的研究。主要参考《构建高性能Web站点》一书。

目录

  • 什么是服务器并发处理能力
  • 怎样衡量服务器并发处理能力
    1. 吞吐率
    2. 压力测试
  • 怎么提高服务器的并发处理能力
    1. 提高CPU并发计算能力
      • 多进程 & 多线程
      • 减少进程切换,使用线程,考虑进程绑定CPU
      • 减少使用不必要的锁,考虑无锁编程
      • 考虑进程优先级
      • 关注系统负载
      • 关注CPU使用率,除了用户空间和内核空间的CPU使用率以外,还要关注I/O wait
    2. 考虑减少内存分配和释放
      1. 改善数据结构和算法复杂度
      2. 使用内存池
      3. 考虑使用共享内存
    3. 考虑使用持久连接
    4. 改进I/O模型
      • DMA技术
      • 异步I/O
      • 多路I/O复用: epoll
      • sendfile
      • 内存映射
      • 直接I/O
    5. 改进服务器并发策略
      • 一个进程处理一个连接,非阻塞I/O
      • 一个线程处理一个连接,非阻塞IO
      • 一个进程处理多个连接,异步I/O
      • 一个线程处理多个连接,异步IO
    6. 改进硬件环境

什么是服务器并发处理能力

一台服务器在单位时间里能处理的请求越多,服务器的能力越高,也就是服务器并发处理能力越强

有什么方法衡量服务器并发处理能力

1. 吞吐率

吞吐率,单位时间里服务器处理的最大请求数,单位req/s

从服务器角度,实际并发用户数的可以理解为服务器当前维护的代表不同用户的文件描述符总数,也就是并发连接数。服务器一般会限制同时服务的最多用户数,比如apache的MaxClents参数。

这里再深入一下,对于服务器来说,服务器希望支持高吞吐率,对于用户来说,用户只希望等待最少的时间,显然,双方不能满足,所以双方利益的平衡点,就是我们希望的最大并发用户数。

2. 压力测试

有一个原理一定要先搞清楚,假如100个用户同时向服务器分别进行10个请求,与1个用户向服务器连续进行1000次请求,对服务器的压力是一样吗?实际上是不一样的,因对每一个用户,连续发送请求实际上是指发送一个请求并接收到响应数据后再发送下一个请求。这样对于1个用户向服务器连续进行1000次请求, 任何时刻服务器的网卡接收缓冲区中只有1个请求,而对于100个用户同时向服务器分别进行10个请求,服务器的网卡接收缓冲区最多有100个等待处理的请求,显然这时的服务器压力更大。

压力测试前提考虑的条件

  • 并发用户数: 指在某一时刻同时向服务器发送请求的用户总数(HttpWatch)
  • 总请求数
  • 请求资源描述
  • 请求等待时间(用户等待时间)
  • 用户平均请求的等待时间
  • 服务器平均请求处理的时间
  • 硬件环境

压力测试中关心的时间又细分以下2种:

  1. 用户平均请求等待时间(这里暂不把数据在网络的传输时间,还有用户PC本地的计算时间计算入内)
  2. 服务器平均请求处理时间

用户平均请求等待时间主要用于衡量服务器在一定并发用户数下,单个用户的服务质量;而服务器平均请求处理时间就是吞吐率的倒数,一般来说,用户平均请求等待时间 = 服务器平均请求处理时间 * 并发用户数

怎么提高服务器的并发处理能力

1. 提高CPU并发计算能力

服务器之所以可以同时处理多个请求,在于操作系统通过多执行流体系设计使得多个任务可以轮流使用系统资源,这些资源包括CPU,内存以及I/O. 这里的I/O主要指磁盘I/O, 和网络I/O。

多进程 & 多线程

多执行流的一般实现便是进程,多进程的好处可以对CPU时间的轮流使用,对CPU计算和IO操作重叠利用。这里的IO主要是指磁盘IO和网络IO,相对CPU而言,它们慢的可怜。

而实际上,大多数进程的时间主要消耗在I/O操作上。现代计算机的DMA技术可以让CPU不参与I/O操作的全过程,比如进程通过系统调用,使得CPU向网卡或者磁盘等I/O设备发出指令,然后进程被挂起,释放出CPU资源,等待I/O设备完成工作后通过中断来通知进程重新就绪。对于单任务而言,CPU大部分时间空闲,这时候多进程的作用尤为重要。

多进程不仅能够提高CPU的并发度。其优越性还体现在独立的内存地址空间和生命周期所带来的稳定性和健壮性,其中一个进程崩溃不会影响到另一个进程。

但是进程也有如下缺点:

  1. fork()系统调用开销很大: prefork
  2. 进程间调度和上下文切换成本: 减少进程数量
  3. 庞大的内存重复:共享内存
  4. IPC编程相对比较麻烦
减少进程切换

当硬件上下文频繁装入和移出时,所消耗的时间是非常可观的。可用Nmon工具监视服务器每秒的上下文切换次数。

为了尽量减少上下文切换次数,最简单的做法就是减少进程数,尽量使用线程并配合其它I/O模型来设计并发策略。

还可以考虑使用进程绑定CPU技术,增加CPU缓存的命中率。若进程不断在各CPU上切换,这样旧的CPU缓存就会失效。

减少使用不必要的锁

服务器处理大量并发请求时,多个请求处理任务时存在一些资源抢占竞争,这时一般采用“锁”机制来控制资源的占用,当一个任务占用资源时,我们锁住资源,这时其它任务都在等待锁的释放,这个现象称为锁竞争。

通过锁竞争的本质,我们要意识到尽量减少并发请求对于共享资源的竞争。比如在允许情况下关闭服务器访问日志,这可以大大减少在锁等待时的延迟时间。要最大程度减少无辜的等待时间。

这里说下无锁编程,就是由内核完成这个锁机制,主要是使用原子操作替代锁来实现对共享资源的访问保护 ,使用原子操作时,在进行实际的写操作时,使用了lock指令,这样就可以阻止其他任务写这块内存,避免出现数据竞争现象。原子操作速度比锁快,一般要快一倍以上。

例如fwrite(), fopen(),其是使用append方式写文件,其原理就是使用了无锁编程,无锁编程的复杂度高,但是效率快,而且发生死锁概率低。

考虑进程优先级

进程调度器会动态调整运行队列中进程的优先级,通过top观察进程的PR值

考虑系统负载

可在任何时刻查看/proc/loadavg, top中的load average也可看出

考虑CPU使用率

除了用户空间和内核空间的CPU使用率以外,还要关注I/O wait,它是指CPU空闲并且等待I/O操作完成的时间比例(top中查看wa的值)。

2. 考虑减少内存分配和释放

服务器的工作过程中,需要大量的内存,使得内存的分配和释放工作尤为重要。

可以通过改善数据结构和算法复制度来适当减少中间临时变量的内存分配及数据复制时间,而服务器本身也使用了各自的策略来提高效率。

例如Apache,在运行开始时一次申请大片的内存作为内存池,若随后需要时就在内存池中直接获取,不需要再次分配,避免了频繁的内存分配和释放引起的内存整理时间。

再如Nginx使用多线程来处理请求,使得多个线程之间可以共享内存资源,从而令它的内存总体使用量大大减少,另外,nginx分阶段的内存分配策略,按需分配,及时释放,使得内存使用量保持在很小的数量范围。

另外,还可以考虑共享内存。

共享内存指在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存,也可以由不同进程共享,是非常快的进程通信方式。

但是使用共享内存也有不好的地方,就是对于多机器时数据不好统一。

shell命令ipcs可用来显示系统下共享内存的状态,函数shmget可以创建或打开一块共享内存区,函数shmat将一个存在的共享内存段连接到本进程空间, 函数shmctl可以对共享内存段进行多种操作,函数shmdt函数分离该共享内存。

3. 考虑使用持久连接

持久连接也为长连接,它本身是TCP通信的一种普通方式,即在一次TCP连接中持续发送多分数据而不断开连接,与它相反的方式称为短连接,也就是建立连接后发送一份数据就断开,然后再次建立连接发送下一份数据, 周而复始。是否采用持久连接,完全取决于应用特点。从性能角度看,建立TCP连接的操作本身是一项不小的开销,在允许的情况下,连接次数越少,越有利于性能的提升; 尤其对于密集型的图片或网页等小数据请求处理有明显的加速所用。

HTTP长连接需要浏览器和web服务器的共同协作,目前浏览器普遍支持长连接,表现在其发出的HTTP请求数据头中包含关于长连接的声明,如下: Connection: Keep-Alive

主流的web服务器都支持长连接,比如apache中,可以用KeepAlive off关闭长连接。

对于长连接的有效使用,还有关键一点在于长连接超时时间的设置,即长连接在什么时候关闭吗? Apache的默认设置为5s, 若这个时间设置过长,则可能导致资源无效占有,维持大量空闲进程,影响服务器性能。

4. 改进I/O 模型

I/O操作根据设备的不同分为很多类型,比如内存I/O, 网络I/O, 磁盘I/O. 对于网络I/O和磁盘I/O, 它们的速度要慢很多,尽管使用RAID磁盘阵列可通过并行磁盘磁盘来加快磁盘I/O速度,购买大连独享网络带宽以及使用高带宽网络适配器可以提高网络i/O的速度。但这些I/O操作需要内核系统调用来完成,这些需要CPU来调度,这使得CPU不得不浪费宝贵的时间来等待慢速I/O操作。我们希望让CPU足够少的时间在i/O操作的调度上,如何让高速的CPU和慢速的I/O设备更好地协调工作,是现代计算机一直探讨的话题。各种I/O模型的本质区别在于CPU的参与方式。

1. DMA技术

I/O设备和内存之间的数据传输方式由DMA控制器完成。在DMA模式下,CPU只需向DMA下达命令,让DMA控制器来处理数据的传送,这样可以大大节省系统资源。

2. 异步I/O

异步I/O指主动请求数据后便可以继续处理其它任务,随后等待I/O操作的通知,这样进程在数据读写时不发生阻塞。

异步I/O是非阻塞的,当函数返回时,真正的I/O传输已经完成,这让CPU处理和I/O操作达到很好的重叠。

3. I/O多路复用

epoll服务器同时处理大量的文件描述符是必不可少的,若采用同步非阻塞I/O模型,若同时接收TCP连接的数据,就必须轮流对每个socket调用接收数据的方法,不管这些socket有没有可接收的数据,都要询问一次。假如大部分socket并没有数据可以接收,那么进程便会浪费很多CPU时间用于检查这些socket有没有可以接收的数据。多路I/O就绪通知的出现,提供了对大量文件描述符就绪检查的高性能方案,它允许进程通过一种方法同时监视所有文件描述符,并可以快速获得所有就绪的文件描述符,然后只针对这些文件描述符进行数据访问。

epoll可以同时支持水平触发和边缘触发,理论上边缘触发性能更高,但是代码实现复杂,因为任何意外的丢失事件都会造成请求处理错误。

epoll主要有2大改进:

  1. epoll只告知就绪的文件描述符,而且当调用epoll_wait()获得文件描述符时,返回并不是实际的描述符,而是一个代表就绪描述符数量的值,然后只需去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里使用了内存映射(mmap)技术,这样彻底省掉了这些文件描述符在系统调用时复制的开销。
  2. epoll采用基于事件的就绪通知方式。其事先通过epoll_ctrl()注册每一个文件描述符,一旦某个文件描述符就绪时,内核会采用类似callback的回调机制,当进程调用epoll_wait()时得到通知

关于IO模型,可以参考笔者前面写的相关文章Java NIO.2; 关于epoll,可以参考笔者前面写的文章select、poll和epoll简介

4. Sendfile

大多数时候,我们都向服务器请求静态文件,比如图片,样式表等,在处理这些请求时,磁盘文件的数据先经过内核缓冲区,然后到用户内存空间,不需经过任何处理,其又被送到网卡对应的内核缓冲区,接着再被送入网卡进行发送。

Linux提供sendfile()系统调用,可以讲磁盘文件的特定部分直接传送到代表客户端的socket描述符,加快了静态文件的请求速度,同时减少CPU和内存的开销。

适用场景: 对于请求较小的静态文件,sendfile发挥的作用不那么明显,因发送数据的环节在整个过程中所占时间的比例相比于大文件请求时小很多。

5. 内存映射

Linux内核提供一种访问磁盘文件的特殊方式,它可以将内存中某块地址空间和我们指定的磁盘文件相关联,从而对这块内存的访问转换为对磁盘文件的访问。这种技术称为内存映射。

多数情况下,内存映射可以提高磁盘I/O的性能,无须使用read()或write()等系统调用来访问文件,而是通过mmap()系统调用来建立内存和磁盘文件的关联,然后像访问内存一样自由访问文件。

缺点:在处理较大文件时,内存映射会导致较大的内存开销,得不偿失。

6. 直接I/O

在linux 2.6中,内存映射和直接访问文件没有本质差异,因为数据需要经过2次复制,即在磁盘与内核缓冲区之间以及在内核缓冲区与用户态内存空间。

引入内核缓冲区的目的在于提高磁盘文件的访问性能,然而对于一些复杂的应用,比如数据库服务器,它们为了进一步提高性能,希望绕过内核缓冲区,由自己在用户态空间实现并管理I/O缓冲区,比如数据库可根据更加合理的策略来提高查询缓存命中率。另一方面,绕过内核缓冲区也可以减少系统内存的开销,因内核缓冲区本身就在使用系统内存。

Linux在open()系统调用中增加参数选项O_DIRECT,即可绕过内核缓冲区直接访问文件,实现直接I/O。

在Mysql中,对于Innodb存储引擎,自身进行数据和索引的缓存管理,可在my.cnf配置中分配raw分区跳过内核缓冲区,实现直接I/O。

改进服务器并发策略

服务器并发策略的目的,是让I/O操作和CPU计算尽量重叠进行,一方面让CPU在I/O等待时不要空闲,另一方面让CPU在I/O调度上尽量花最少的时间。

一个进程处理一个连接,非阻塞I/O

这样会存在多个并发请求同时到达时,服务器必然要准备多个进程来处理请求。其进程的开销限制了它的并发连接数。但从稳定性和兼容性的角度,则其相对安全,任何一个子进程的崩溃不会影响服务器本身,父进程可以创建新的子进程;这种策略典型的例子就是Apache的fork和prefork模式。对于并发数不高(如150以内)的站点同时依赖Apache其它功能时的应用选择Apache还是可以的。

一个线程处理一个连接,非阻塞IO

这种方式允许在一个进程中通过多个线程来处理多个连接,一个线程处理一个连接。Apache的worker模式就是这种典型例子,使其可支持更多的并发连接。不过这种模式的总体性能还不如prefork,所以一般不选用worker模式。

一个进程处理多个连接,异步I/O

一个线程同时处理多个连接,潜在的前提条件就是使用IO多路复用就绪通知。

这种情况下,将处理多个连接的进程叫做worker进程或服务进程。worker的数量可以配置,如Nginx中的worker_processes 4。

一个线程处理多个连接,异步IO

即使有高性能的IO多路复用就绪通知,但磁盘IO的等待还是无法避免的。更加高效的方法是对磁盘文件使用异步IO,目前很少有Web服务器真正意义上支持这种异步IO。

6. 改进硬件环境

还有一点要提及的是硬件环境,服务器的硬件配置对应用程序的性能提升往往是最直接,也是最简单的方式,这就是所谓的scale up。这里不做论述。

Delicious Save this on Delicious

Related Posts

tuxedo(三)-tuxedo与oracle连接配置

tuxedo(二)中已经基本启动了tuxedo服务,只是与oracle的连接还未配置好,如果配置完成就可以完成并开发最基本的三层服务应用结构。本节将介绍与oracle12c  pdb 数据库的连接配置。我们使用XA接口,因为该模式可以与多个数据库连接。

一.oracle数据库配置准备

oracle12c 引入了CDB与PDB新特性,该特性详见 ORACLE 12C新特性——CDB与PDB。

我们在PDB上新建用户表空间及权限用于tuxedo 系统连接使用。

1.启动数据库监听

修改监听及TNS配置然后启动监听

添加list_listener sid:

同时当PDB启动后,会动态的默认启动pdborcl sid数据库的监听。当然也可以静态的配置上去

/oracle/11g/network/admin/listener.ora:

 

[bath]#ADD BY ZWR SID_LIST_LISTENER = (SID_LIST = (SID_DESC = (SID_NAME = PLSExtProc) (ORACLE_HOME =/oracle/11g) (PROGRAM = extproc) ) (SID_DESC = (GLOBAL_DBNAME = orcl) (ORACLE_HOME =/oracle/11g) (SID_NAME = orcl) ) ) [/bath]

启动监听

[bath]lsnrctl status lsnrctl stop lsnrctl start [/bath]

image

启动数据库

sqlplus /nolog 

conn sys/Mwb123456 as sysdba startup 

2.创建PDB表空间用户

使用sqlplus 登陆到 CDB表容器后,先登陆到PDB

切换PDB数据库

C## user zwruser identified by zwruser default tablespace zwr temporary tablespace zwr_temp;

alter session set container=pdborcl;

startup

创建表空间用户

create tablespace zwr logging datafile'/oracle/oradata/orcl/zwr1.dbf' size 100m autoextend on next 100m maxsize 10240m extent management local;

create temporary tablespace zwr_temp tempfile'/oracle/oradata/orcl/zwr_temp1.dbf ' size 100m autoextend on next 100m maxsize 10240m extent management local;

create user zwruser identified by zwruser default tablespace zwr temporary tablespace zwr_temp;

grant create user,drop user,alter user,create any view,connect,resource,dba,create session,create any sequence to zwruser ;

使用新建用户连接数据库

conn zwruser/zwruser@//localhost:1521/pdborcl as sysdba

二.tuxedo 连接配置

1.配置ubbsimple 内Oralce_XA连接参数:

OPENINFO="Oracle_XA:Oracle_XA+Acc=P/zwruser/zwruser+SqlNet=pdborcl+SesTm=600+MaxCur=5+LogDir=."

连接用户名:

Acc=P/zwruser/zwruser:用户名,密码Acc=P/zwruser as sysdba/zwruser

数据库TNS名称:

SqlNet=pdborcl:数据库名称

2.修改tnsnames,添加 pdborcl  :

/oracle/11g/network/admin/tnsnames.ora:

ORCL = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = orcl.localdomain) ) ) pdborcl = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = pdborcl.localdomain) ) ) 

三:启动调试

tmboot -y

启动texedo 服务:

image

以上表示启动成功,如果红框内显示Fail,则在配置的XA配置的log目录下查看日志信息:

ULOG.120813以及xa 开头的日志。

 

以上就已经完成 tuxedo服务与数据库的连接。

tuxedo(二)-tuxedo安装配置

前面一节准备好了系统数据库环境,然后我们就可以安装tuxedo了。

一、创建用户

root 用户下创建用户组及tuxedo用户

groupadd tux useradd tuxedo -g tux -G dba 

passwd tuxedo

二、tuxedo 安装

tuxedo下载

[bath]sh tuxedo111120_64_Linux_01_x86.bin -i console[/bath]

tuxedo111120_64_Linux_01_x86.bin:http://download.oracle.com/otn/bea/tuxedo/11g/111120/tuxedo111120_64_Linux_01_x86.bin?AuthParam=1477891570_b560b9a7eac57fd458be753ed0f84778

[bath]sh tuxedo111120_64_Linux_01_x86.bin -i console[/bath]

安装语言,选择English,默认回车

image

软件介绍,有警告信息,跳过,回车

image

选择安装模式,默认全部安装,回车

image

选择oracle home目录,输入准备安装的路径:/home/tuxedo/oracle

image

是否安装样例,选择是

image

确认安装信息后安装:tuxedo 安装到 /home/tuxedo/oracle/tuxedo11gR1

至此tuxedo 中间件已经安装到系统中,下面确认tuxedo 服务的运行配置

三、 tuxedo服务配置

我们使用 样例中的 samples/atmi/simpapp 来配置服务及验证系统。

重新设置服务目录,将样例拷贝到该服务目录:

/home/tuxedo/oracle/tuxedoapp/src/simpapp。

1.配置环境变量:更改使用

[bath]sh /home/tuxedo/oracle/tuxedo11gR1/tux.env[/bath]

[bath]TUXDIR=/home/tuxedo/oracle/tuxedo11gR1; export TUXDIR JAVA_HOME=$TUXDIR/jre; export JAVA_HOME JVMLIBS=$JAVA_HOME/lib/amd64/server:$JAVA_HOME/jre/bin ORACLE_HOME=/oracle/11g;export ORACLE_HOME ORACLE_BASE=/oracle;export ORACLE_BASE PATH=$ORACLE_HOME/bin:$TUXDIR/bin:$JAVA_HOME/bin:$PATH; export PATH COBCPY=:$TUXDIR/cobinclude; export COBCPY COBOPT=”-C ANS85 -C ALIGN=8 -C NOIBMCOMP -C TRUNC=ANSI -C OSEXT=cbl”; export COBOPT SHLIB_PATH=$TUXDIR/lib:$JVMLIBS:$SHLIB_PATH; export SHLIB_PATH LIBPATH=$TUXDIR/lib:$JVMLIBS:$LIBPATH; export LIBPATH LD_LIBRARY_PATH=$TUXDIR/lib:$JVMLIBS:$LD_LIBRARY_PATH; export LD_LIBRARY_PATH WEBJAVADIR=$TUXDIR/udataobj/webgui/java; export WEBJAVADIR TUXCONFIG=/home/tuxedo/oracle/tuxedoapp/src/simpapp/tuxconfig;export TUXCONFIG [/bath]

2.配置服务ubbsimple文件如下:

特别要注意的有:#add by zwr 的项及与oracle 数据连接的配置OPENINFO,TMSNAME。

#    (c) 2003 BEA Systems, Inc. All Rights Reserved.
#ident    “@(#) samples/atmi/simpapp/ubbsimple    $Revision: 1.7 $”

#Skeleton UBBCONFIG file for the TUXEDO Simple Application.
#Replace the <bracketed> items with the appropriate values.

*RESOURCES
#IPCKEY        <Replace with a valid IPC Key>

#Example:
IPCKEY        123456

DOMAINID    simpapp
MASTER        simple
MAXACCESSERS    10
MAXSERVERS    5
MAXSERVICES    10
MODEL        SHM
LDBAL        N

*MACHINES
DEFAULT:
        APPDIR=”/home/tuxedo/oracle/tuxedoapp/src/simpapp”
        TUXCONFIG=”/home/tuxedo/oracle/tuxedoapp/src/simpapp/tuxconfig”
        TUXDIR=”/home/tuxedo/oracle/tuxedo11gR1″
#Example:
#        APPDIR=”/home/me/simpapp”
#        TUXCONFIG=”/home/me/simpapp/tuxconfig”
#        TUXDIR=”/usr/tuxedo”
#add by zwr xa
TLOGDEVICE= “/home/tuxedo/oracle/tuxedoapp/TLOG”
TLOGNAME=TLOG
TLOGSIZE=200
“localhost.localdomain”    LMID=simple

#Example:
#beatux        LMID=simple

*GROUPS
GROUP1
    LMID=simple    GRPNO=1
OPENINFO=”Oracle_XA:Oracle_XA+Acc=P/zwruser/zwruser+SqlNet=pdborcl+SesTm=600+MaxCur=5+LogDir=.”
TMSNAME=”TMS_ORA11g” TMSCOUNT=2
*SERVERS
DEFAULT:
        CLOPT=”-A”

simpserv    SRVGRP=GROUP1 SRVID=1

*SERVICES
TOUPPER

 

3.生成服务配置

[bath]$tmloadcf -y ubbsimple[/bath]

4.生成与数据库连接的TMS_ORA11g

[bath]buildtms -o $ORACLE_HOME/bin/TMS_ORA11g -r Oracle_XA[/bath]

5.编译服务

[bath]buildserver -o simpserv -f simpserver.c -r Oracle_XA -s TOUPPER[/bath]

6.启动关闭服务

[bath]tmboot -y tmshutdown -y [/bath]

7.编译客户端并测试

[bath]buildclient -o simpcl -f simpcl.c[/bath]

$./simpcl hello 显示Returned string is: HELLO表示测试成功

tuxedo(一)-服务器准备vmware12,oracle12c

tuxedo 中间件一般与oracle 数据结合使用,构成一个3层的分布式服务框架,现在就安装一下tuxedo 所准备的环境,centos6.5、oracle12c,为了测试方便我们将其安装到VMware中。

软件版本

VMware 12,oracle12c,centos6.5

安装

一、安装VMWare12

没什么说的基本上就是选择文件夹和下一步,最后需要输入License,5A02H-AU243-TZJ49-GTC7K-3C61N。

二.安装CentOS 6.5

没什么好说的

三、安装Oracle

1.下载oracle11c,zip文件,一起解压即可

2.使用root用户登录,并通过yum安装必须软件

Shell代码 收藏代码

  1. # yum install binutils compat-libstdc++-33 elfutils elfutils-libelf-devel gcc gcc-c++ glibc glibc-common glibc-devel glibc-headers libaio libaio-devel libgcc libstdc++ libstdc++-devel make sysstat unixODBC unixODBC-devel 

3.新建oracle用户及oracle安装文件目录

Shell代码 收藏代码

  1. #groupadd oinstall  
  2. #groupadd dba  
  3. #useradd -g oinstall -G dba oracle  
  4. #passwd oracle  
  5. #mkdir /oracle  
  6. #mkdir /oracle/11g  
  7. #chown -R oracle:oinstall /oracle 

4.修改系统环境变量

这一步用vi其实比较麻烦,推荐用sFtp工具将文件拷贝出来修改

另,附件中有要修改的文件备份,和修改结果

1).#vi /etc/sysctl.conf

Shell代码 收藏代码

  1. kernel.shmall = 2097152
  2. kernel.shmmax = 2147483648
  3. kernel.shmmni = 4096
  4. kernel.sem= 250 32000 100 128
  5. fs.file-max=6815744
  6. net.ipv4.ip_local_port_range = 9000 65500
  7. net.core.rmem_default = 262144
  8. net.core.rmem_max = 4194304
  9. net.core.wmem_default = 262144
  10. net.core.wmem_max = 10488576
  11. fs.aio-max-nr = 10488576

2).#vi /etc/security/limits.conf

Shell代码 收藏代码

  1. oracle  soft    nproc   2047
  2. oracle  hard    nproc   16384
  3. oracle  soft    nofile  1024
  4. oracle  hard    nofile  65536

3).vi /etc/pam.d/login

Shell代码 收藏代码

  1. session required /lib/security/pam_limits.so  
  2. session required pam_limits.so 

4).vi /etc/profile

Java代码 收藏代码

  1. if [ $USER = “oracle” ]; then  
  2. if [ $SHELL = “/bin/ksh” ]; then  
  3.         ulimit -p 16384
  4.         ulimit -n 65536
  5. else
  6.         ulimit -u 16384 -n 65536
  7.     fi  
  8. fi 

5).cd /home/oracle

vi .bash_profile

Java代码 收藏代码

  1. ORACLE_BASE=/oracle  
  2. ORACLE_HOME=$ORACLE_BASE/11g  
  3. –下面的配置是安装oracle后新建的oracle实例的名字  
  4. ORACLE_SID=orcl  
  5. PATH=$ORACLE_HOME/bin:$PATH  
  6. export ORACLE_BASE ORACLE_HOME ORACLE_SID PATH 

6).关闭SELinux,安装完成后可以打开

#vim /etc/selinux/config 确保以下内容

Shell代码 收藏代码

  1. SELINUX=disabled 

5.安装oracle

用oracle用户进入linux,进入终端,并进入安装文件目录(这个目录放在哪里都可以,就是一开始两个压缩解压缩的目录)

Shell代码 收藏代码

  1. $cd oralce 安装文件目录 

–下一步骤是为了防止oracle安装过程中的中文乱码问题

Shell代码 收藏代码

  1. $LANG=en 

开始安装,如果提示Permission denied,用chmod设置一下权限

Shell代码 收藏代码

  1. $chmod 777 database -R  
  2. $cd database  
  3. $sh runInstaller 

安装过程是图形界面,按照提示一直安装就行。安装过程中会要求你用root用户执行命令root.sh和orainstRoot.sh,自己照着提示的完整路径做就是。还有会要求制订一个inventory目录,随便哪个位置都行。






Shell代码 收藏代码

  1. wget http://mirror.centos.org/centos/5/os/x86_64/CentOS/pdksh-5.2.14-37.el5_8.1.x86_64.rpm  
  2. rpm -ivh pdksh-5.2.14-37.el5_8.1.x86_64.rpm 

中间出现Password Management直接选择OK

然后等待提示用root用户执行sh,中间输入地址直接回车。

6.验证

(1)系统重启后启动oracle过程

在oracle用户下,进入终端输入

Shell代码 收藏代码

  1. $ sqlplus  
  2. SQL*Plus: Release 11.2.0.1.0 Production on Mon Feb 20 22:46:00 2012
  3. Copyright (c) 1982, 2009, Oracle.  All rights reserved.  
  4. Enter user-name: sys as sysdba  
  5. Enter password:    

输入正确的用户名和密码,注意sys用户登录的话必须加上 as sysdba

然后启动oracle服务:

Sqlplus代码 收藏代码

  1. sql> startup  
  2. sql> exit 

关闭Oracle服务

Sqlplus代码 收藏代码

  1. sql> shutdown immediate 

(2)再启动oracle监听程序

Shell代码 收藏代码

  1. $ cd $ORACLE_HOME/bin  
  2. $ lsnrctl start 

测试

重新打开一个终端,如下输入:

Shell代码 收藏代码

  1. $ sqlplus sys as sysdba @localhost :1521/orcl 

输入密码,进入到oracle的sqlplus命令行下,执行一个简单的查询(显示所有的用户):

Sqlplus代码 收藏代码

  1. SQL> select * from all_users; 

或执行:

Sqlplus代码 收藏代码

  1. SQL> select table_name from user_tables 

另:

1.创建一个具有DBA权限的用户

(1)以管理员身份进入数据库 SQLPLUS SYSTEM/密码

(2)创建用户 CREATE USER 用户名 IDENTIFIED BY 密码;

Sqlplus代码 收藏代码

  1. SQL> create user sgq0085 identified by 123456;  
  2. User created. 

(3)将用户上锁/解锁  ALTER USER 用户名 ACCOUNT UNLOCK/LOCK;

Sqlplus代码 收藏代码

  1. SQL> alter user sgq0085 account lock;  
  2. User altered.  
  3. SQL> alter user sgq0085 account unlock;  
  4. User altered. 

(4)授予新创建的用户登录权限 GRANT CREATE SESSION TO 用户名;

Sqlplus代码 收藏代码

  1. SQL> grant create session to sgq0085;  
  2. Grant succeeded. 

(5)授予新创建的用户数据库管理员权限 GRANT DBA TO 用户名;

Sqlplus代码 收藏代码

  1. SQL> grant dba to sgq0085;  
  2. Grant succeeded.  
  3. SQL> select * from dba_users; 

(6)切换到新创建的用户登录 CONNECT 用户名/密码;

Sqlplus代码 收藏代码

  1. SQL> connect sgq0085/123456;  
  2. Connected. 

(7)删除用户 DROP USER 用户名

Sqlplus代码 收藏代码

  1. SQL> drop user sgq0085;  
  2. User dropped. 

2.指定开放系统端口

Shell代码 收藏代码

  1. # su – root  
  2. # vi /etc/sysconfig/iptables 

希望开放的端口写为如下这种

-A INPUT -m state –state NEW -m tcp -p tcp –dport 80 -j ACCEPT

-A INPUT -m state –state NEW -m tcp -p tcp –dport 1158 -j ACCEPT

-A INPUT -m state –state NEW -m tcp -p tcp –dport 1521 -j ACCEPT

vi中复制一行的方法

Vi代码 收藏代码

  1. 1)把光标移动到要复制的行上 2)按yy 3)把光标移动到要复制的位置 4)按p 

重启iptables

Shell代码 收藏代码

  1. # service iptables restart 

检测

Shell代码 收藏代码

  1. # iptables -L 

3.设置系统时间和时区

Shell代码 收藏代码

  1. [root@localhost ~]# date -s 09/29/2013
  2. Sun Sep 29 00:00:00 CST 2013
  3. [root@localhost ~]# date -s 09/29/2013
  4. Sun Sep 29 00:00:00 CST 2013
  5. [root@localhost ~]# cp -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime  
  6. cp: overwrite `/etc/localtime’? y  
  7. [root@localhost ~]# clock -w  
  8. [root@localhost ~]# date -R  
  9. Sun, 29 Sep 2013 00:02:01 +0800
  10. [root@localhost ~]#  

4.NAT 固定IP地址

/etc/sysconfig/network-scripts/ifcfg-eth0

Txt代码 收藏代码

  1. DEVICE=”eth0″
  2. BOOTPROTO=”static” #还有一种是dhcp  
  3. BROADCAST=192.168.130.255 #广播地址  
  4. NETWORK=192.168.130.0 #网段  
  5. IPADDR=192.168.130.128 #静态IP  
  6. NETMASK=255.255.255.0 #子网掩码  
  7. HWADDR=”00:0C:29:08:5F:12″
  8. IPV6INIT=”yes”
  9. NM_CONTROLLED=”yes”
  10. ONBOOT=”yes”
  11. TYPE=”Ethernet”
  12. UUID=”ebfda46e-4e37-4126-8b30-96b7650843de”

/etc/sysconfig/network

Txt代码 收藏代码

  1. NETWORKING=yes  
  2. HOSTNAME=localhost.localdomain  
  3. GATEWAY=192.168.130.2 #默认网关 

重启

Shell代码 收藏代码

  1. service network restart  

route add default  gw 192.168.130.2

/etc/resolv.conf

Txt代码 收藏代码

  1. # Generated by NetworkManager  
  2. nameserver 192.168.130.2 #指定DNS  
  3. # No nameservers found; try putting DNS servers into your  
  4. # ifcfg files in /etc/sysconfig/network-scripts like so:  
  5. #  
  6. # DNS1=xxx.xxx.xxx.xxx  
  7. # DNS2=xxx.xxx.xxx.xxx  
  8. # DOMAIN=lab.foo.com bar.foo.com 

防止resolv.conf被修改

Shell代码 收藏代码

  1. chattr +i /etc/resolv.conf 

—–Linux下查看及更改oracle字符集编码
[root@OracleDB ~]# cd /usr/local/oracle/
[root@OracleDB oracle]# env|grep NLS_LANG
NLS_LANG=american_america.zhs16gbk
[root@OracleDB oracle]# vi .bash_profile
# 使 bash_profile 设置生效
source .bash_profile

# 常用unicode字符集
export NLS_LANG=american_america.AL32UTF8
# 常用中文字符集
export
可以编辑 bash_profile 文件进行永久设置
vi .bash_profile
export NLS_LANG=”SIMPLIFIED CHINESE_CHINA.ZHS16GBK”

or export NLS_LANG=”Simplified Chinese_china”.ZHS16GBK
# 使 bash_profile 设置生效

單一使用者可進行 root 所有指令(sudo)

Shell代码 收藏代码

  1. [root@~]# visudo  
  2. ….(前面省略)….  
  3. root    ALL=(ALL)       ALL  <==找到這一行,大約在 76 行左右  
  4. vbird1  ALL=(ALL)       ALL  <==這一行是你要新增的!  
  5. ….(前面省略)…. 

vbird1 是账号名