当前位置:文档之家› 用Java Socket开发高并发小型服务器

用Java Socket开发高并发小型服务器

用Java Socket开发高并发小型服务器
用Java Socket开发高并发小型服务器

用Java Socket开发高并发小型服务器

Java Socket

套接字(socket)为两台计算机之间的通信提供了一种机制,在James Gosling注意到Java 语言之前,套接字就早已赫赫有名。该语言只是让您不必了解底层操作系统的细节就能有效地使用套接字。

1 客户机/服务器模型

在饭店里,菜单上各种具有异国情调的食品映入你的眼帘,于是你要了一份pizza。几分钟后,你用力咀嚼浇着融化的乳酪和其他你喜欢的配料的热pizza。你不知道,也不想知道:侍者从那里弄来了pizza,在制作过程中加进了什么,以及配料是如何获得的。

上例中包含的实体有:美味的pizza、接受你定餐的侍者、制作pizza的厨房,当然还有你。你是定pizza的顾客或客户。制作pizza的过程对于你而言是被封装的。你的请求在厨房中被处理,pizza制作完成后,由侍者端给你。

你所看到的就是一个客户机/服务器模型。客户机向服务器发送一个请求或命令。服务器处理客户机的请求。客户机和服务器之间的通讯是客户机/服务器模型中的一个重要组成部分,通常通过网络进行。

客户机/服务器模型是一个应用程序开发框架,该框架是为了将数据的表示与其内部的处理和存储分离开来而设计的。客户机请求服务,服务器为这些请求服务。请求通过网络从客户机传递到服务器。服务器所进行的处理对客户机而言是隐藏的。一个服务器可以为多台客户机服务。

多台客户机访问服务器

服务器和客户机不一定是硬件组件。它们可以是工作啊同一机器或不同机器上的程序。、

考虑一个航空定票系统中的数据输入程序:数据----乘客名、航班号、飞行日期、目的地等可以被输入到前端----客户机的应用程序中。一旦数据输入之后,客户机将数据发送到后端----服务器端。服务器处理数据并在数据库中保存数据。客户机/服务器模型的重要性在于所有的数据都存放在同一地点。客户机从不同的地方访问同一数据源,服务器对所有的输入数据应用同样的检验规则。

万维网为…为什么要将数据的表示与其存储、处理分离开来?提供了一个很好的例子。在Web 上,你无需控制最终用户用来访问你数据的平台和软件。你可以考虑编写出适用与每一种潜在的目标平台的应用程序。

…客户机/服务器应用程序的服务器部分?管理通过多个客户机访问服务器的、多个用户共享的资源。表明…客户机/服务器程序的服务器部分?强大功能的最好例子应该是Web服务器,它通过Internet将HTML页传递给不同的Web用户。

Java编程语言中最基本的特点是在Java中创建的程序的代码的可移植性。因为具有其他语言所不具备的代码可移植性,Java允许用户只要编写一次应用程序,就可以在任何客户机系统上发布它,并可以让客户机系统解释该程序。这意味着:你只要写一次代码,就能使其在任何平台上运行。

2 协议

当你同朋友交谈时,你们遵循一些暗含的规则(或协议)。例如:你们俩不能同时开始说话,或连续不间断地说话。如果你们这样作的话,谁也不能理解对方所说的东西。当你说话时,你的朋友倾听,反之亦然。你们以双方都能理解的语言和速度进行对话。

当计算机之间进行通讯的时候,也需要遵循一定的规则。数据以包的形式从一台机器发送到另一台。这些规则管理数据打包、数据传输速度和重新数据将其恢复成原始形式。这些规则被称为网络协议。网络协议是通过网络进行通讯的系统所遵循的一系列规则和惯例。连网软件通常实现有高低层次之分的多层协议。网络协议的例子有:TCP/IP、UDP、Apple Talk 和NetBEUI。

Java提供了一个丰富的、支持网络的类库,这些类使得应用程序能方便地访问网络资源。Java提供了两种通讯工具。它们是:使用用户报文协议(UDP)的报文和使用传输控制协议/因特网协议(TCP/IP)的Sockets(套接字)。

数据报包是一个字节数组从一个程序(发送程序)传送到另一个(接受程序)。由于数据报遵守UDP,不保证发出的数据包必须到达目的地。数据报并不是可信赖的。因此,仅当传送少量数据时才使用,而且发送者和接受者之间的距离间隔不大,假如是网络交通高峰,或接受程序正处理来自其他程序的多个请求,就有机会出现数据报包的丢失。

Sockets套接字用TCP来进行通讯。套接字模型同其他模型相比,优越性在于其不受客户

请求来自何处的影响。只要客户机遵循TCP/IP协议,服务器就会对它的请求提供服务。这意味着客户机可以是任何类型的计算机。客户机不再局限为UNIX、Windows、DOS或Macintosh平台,因此,网上所有遵循TCP/IP协议的计算机可以通过套接字互相通讯。

3 Sockets套接字

3.1 Sockets概况

在客户机/服务器应用程序中,服务器提供象处理数据库查询或修改数据库中的数据之类的服务。发生在客户机和服务器之间的通讯必须是可靠的,同时数据在客户机上的次序应该和服务器发送出来的次序相同。

什么是套接字?

既然我们已经知道套接字扮演的角色,那么剩下的问题是:什么是套接字?Bruce Eckel 在他的《Java 编程思想》一书中这样描述套接字:套接字是一种软件抽象,用于表达两台机器之间的连接“终端”。对于一个给定的连接,每台机器上都有一个套接字,您也可以想象它们之间有一条虚拟的“电缆”,“电缆”的每一端都插入到套接字中。当然,机器之间的物理硬件和电缆连接都是完全未知的。抽象的全部目的是使我们无须知道不必知道的细节。

简言之,一台机器上的套接字与另一台机器上的套接字交谈就创建一条通信通道。程序员可以用该通道来在两台机器之间发送数据。当您发送数据时,TCP/IP 协议栈的每一层都会添加适当的报头信息来包装数据。这些报头帮助协议栈把您的数据送到目的地。好消息是Java 语言通过"流"为您的代码提供数据,从而隐藏了所有这些细节,这也是为什么它们有时候被叫做流套接字(streaming socket)的原因。

把套接字想成两端电话上的听筒,我和您通过专用通道在我们的电话听筒上讲话和聆听。直到我们决定挂断电话,对话才会结束(除非我们在使用蜂窝电话)。而且我们各自的电话线路都占线,直到我们挂断电话。

如果想在没有更高级机制如ORB(以及CORBA、RMI、IIOP 等等)开销的情况下进行两台计算机之间的通信,那么套接字就适合您。套接字的低级细节相当棘手。幸运的是,Java 平台给了您一些虽然简单但却强大的更高级抽象,使您可以容易地创建和使用套接字。

传输控制协议(TCP)提供了一条可靠的、点对点的通讯通道,客户机/服务器应用程序可以用该通道互相通讯。要通过TCP进行通讯,客户机和服务器程序建立连接并绑定套接字。

套接字用于处理通过网络连接的应用程序之间的通讯。客户机和服务器之间更深入的通讯通过套接字完成。

Java被设计成一种连网语言。它通过将连接功能封装到套接字类里而使得网络编程更加容易。套接字类即Socket类(它创建一个客户套接字)和ServerSocket类(它创建一个服务器套接字)。套接字类大致介绍如下:

l Socket是基类,它支持TCP协议。TCP是一个可靠的流网络连接协议。Socket类提供了流输入/输出的方法,使得从套接字中读出数据和往套接字中写数据都很容易。该类对于编写因特网上的通讯程序而言是必不可少的。

l ServerSocket是一个因特网服务程序用来监听客户请求的类。ServerSocket实际上并不执行服务;而是创建了一个Socket对象来代表客户机。通讯由创建的对象来完成。

3.2 IP地址和端口

因特网服务器可以被认为是一组套接字类,它们提供了一般称为服务的附加功能。服务的例子有:电子邮件、远程登录的Telnet、和通过网络传输文件的文件传输协议(FTP)。每种服务都与一个端口相联系。端口是一个数值地址,通过它来处理服务请求(就象请求Web 页一样)。

TCP协议需要两个数据项:IP地址和端口号。因此,当键入https://www.doczj.com/doc/ac12474394.html,时,你是如何进入金诺的主页呢?

因特网协议(IP)提供每一项网络设备。这些设备都带有一个称为IP地址的逻辑地址。由因特网协议提供的IP地址具有特定的形式。每个IP地址都是32位的数值,表示4个范围在0到255之间的8位数值金诺已经注册了它的名字,分配给https://www.doczj.com/doc/ac12474394.html,的IP地址为192.168.0.110。

注意:域名服务或DNS服务是将https://www.doczj.com/doc/ac12474394.html,翻译成192.168.0.110的服务。这使你可以键入https://www.doczj.com/doc/ac12474394.html,而不必记住IP地址。想象一下,怎么可能记住所有需要访问的站点的IP地址!有趣的是一个网络名可以映射到许多IP地址。对于经常访问的站点可能需要这一功能,因为这些站点容纳大量的信息,并需要多个IP地址来提供业务服务。例如:192.168.0.110的实际的内部名称为https://www.doczj.com/doc/ac12474394.html,。DNS可以将分配给jinnuo Ltd.的一系列IP地址翻译成https://www.doczj.com/doc/ac12474394.html,。

如果没有指明端口号,则使用服务文件中服务器的端口。每种协议有一个缺省的端口号,在

端口号未指明时使用该缺省端口号。

端口号应用

21 FTP.传输文件

23 Telnet.提供远程登录

25 SMTP.传递邮件信息

67 BOOTP.在启动时提供配置情况

80 HTTP.传输Web页

109 POP.使用户能访问远程系统中的邮箱

让我们再来看一下URL:https://www.doczj.com/doc/ac12474394.html,

URL的第一部分(http)意味着你正在使用超文本传输协议(HTTP),该协议处理Web文档。如果没有指明文件,大多数的Web服务器会取一个叫index.html文件。因此,IP地址和端口既可以通过明确指出URL各部分来决定,也可以由缺省值决定。

4 创建Socket客户

我们将在本部分讨论的示例将阐明在Java 代码中如何使用Socket 和ServerSocket。客户机用Socket 连接到服务器。服务器用ServerSocket 在端口1001 侦听。客户机请求服务器C: 驱动器上的文件内容。

创建RemoteFileClient 类

import java.io.*;

import https://www.doczj.com/doc/ac12474394.html,.*;

public class RemoteFileClient {

protected BufferedReader socketReader;

protected PrintWriter socketWriter;

protected String hostIp;

protected int hostPort;

//构造方法

public RemoteFileClient(String hostIp, int hostPort) {

this.hostIp = hostIp;

this.hostPort=hostPort;

}

//向服务器请求文件的内容

public String getFile(String fileNameToGet) {

StringBuffer fileLines = new StringBuffer();

try {

socketWriter.println(fileNameToGet);

socketWriter.flush();

String line = null;

while((line=socketReader.readLine())!=null)

fileLines.append(line+"\n");

}

catch(IOException e) {

System.out.println("Error reading from file: "+fileNameToGet);

}

return fileLines.toString();

}

//连接到远程服务器

public void setUpConnection() {

try {

Socket client = new Socket(hostIp,hostPort);

socketReader = new BufferedReader(new

InputStreamReader(client.getInputStream()));

socketWriter = new PrintWriter(client.getOutputStream());

}

catch(UnknownHostException e) {

System.out.println("Error1 setting up socket connection: unknown host at "+hostIp+":"+hostPort);

}

catch(IOException e) {

System.out.println("Error2 setting up socket connection: "+e);

}

}

//断开远程服务器

public void tearDownConnection() {

try {

socketWriter.close();

socketReader.close();

}catch(IOException e) {

System.out.println("Error tearing down socket connection: "+e);

}

}

public static void main(String args[]) {

RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1",1001);

remoteFileClient.setUpConnection();

StringBuffer fileContents = new StringBuffer();

fileContents.append(remoteFileClient.getFile("RemoteFileServer.java"));

//remoteFileClient.tearDownConnection();

System.out.println(fileContents);

}

}

首先我们导入https://www.doczj.com/doc/ac12474394.html, 和java.io。https://www.doczj.com/doc/ac12474394.html, 包为您提供您需要的套接字工具。java.io 包为您提供对流进行读写的工具,这是您与TCP 套接字通信的唯一途径。

我们给我们的类实例变量以支持对套接字流的读写和存储我们将连接到的远程主机的详细信息。

我们类的构造器有两个参数:远程主机的IP地址和端口号各一个,而且构造器将它们赋给实例变量。

我们的类有一个main() 方法和三个其它方法。稍后我们将探究这些方法的细节。现在您只需知道setUpConnection() 将连接到远程服务器,getFile() 将向远程服务器请求fileNameToGet 的内容以及tearDownConnection() 将从远程服务器上断开。

实现main()

这里我们实现main() 方法,它将创建RemoteFileClient 并用它来获取远程文件的内容,然后打印结果。main() 方法用主机的IP 地址和端口号实例化一个新RemoteFileClient(客户机)。然后,我们告诉客户机建立一个到主机的连接。接着,我们告诉客户机获取主机上一个指定文件的内容。最后,我们告诉客户机断开它到主机的连接。我们把文件内容打印到控制台,只是为了证明一切都是按计划进行的。

建立连接

这里我们实现setUpConnection() 方法,它将创建我们的Socket 并让我们访问该套接字的流:

public void setUpConnection() {

try {

Socket client = new Socket(hostIp,hostPort);

socketReader = new BufferedReader(new

InputStreamReader(client.getInputStream()));

socketWriter = new PrintWriter(client.getOutputStream());

}

catch(UnknownHostException e) {

System.out.println("Error1 setting up socket connection: unknown host at "+hostIp+":"+hostPort);

}

catch(IOException e) {

System.out.println("Error2 setting up socket connection: "+e);

}

}

setUpConnection() 方法用主机的IP 地址和端口号创建一个Socket:

Socket client = new Socket(hostIp, hostPort);

我们把Socket 的InputStream 包装进BufferedReader 以使我们能够读取流的行。然后,我们把Socket 的OutputStream 包装进PrintWriter 以使我们能够发送文件请求到服务器:

socketReader = new BufferedReader(new

InputStreamReader(client.getInputStream()));socketWriter = new

PrintWriter(client.getOutputStream());

请记住我们的客户机和服务器只是来回传送字节。客户机和服务器都必须知道另一方即将发送的是什么以使它们能够作出适当的响应。在这个案例中,服务器知道我们将发送一条有效的文件路径。

当您实例化一个Socket 时,将抛出UnknownHostException。这里我们不特别处理它,但我们打印一些信息到控制台以告诉我们发生了什么错误。同样地,当我们试图获取Socket 的InputStream 或OutputStream 时,如果抛出了一个一般IOException,我们也打印一些信息到控制台。

与主机交谈

这里我们实现getFile() 方法,它将告诉服务器我们想要什么文件并在服务器传回其内容时接收该内容。

public String getFile(String fileNameToGet) {

StringBuffer fileLines = new StringBuffer();

try {

socketWriter.println(fileNameToGet);

socketWriter.flush();

String line = null;

while((line=socketReader.readLine())!=null)

fileLines.append(line+"\n");

}

catch(IOException e) {

System.out.println("Error reading from file: "+fileNameToGet);

}

return fileLines.toString();

}

对getFile()方法的调用要求一个有效的文件路径String。它首先创建名为fileLines的StringBuffer,fileLines 用于存储我们读自服务器上的文件的每一行。

StringBuffer fileLines = new StringBuffer();

在try{}catch{} 块中,我们用PrintWriter 把请求发送到主机,PrintWriter 是我们在创建连接期间建立的。

socketWriter.println(fileNameToGet); socketWriter.flush();

请注意这里我们是flush() 该PrintWriter,而不是关闭它。这迫使数据被发送到服务器而不关闭Socket。

一旦我们已经写到Socket,我们就希望有一些响应。我们不得不在Socket 的InputStream 上等待它,我们通过在while 循环中调用BufferedReader 上的readLine() 来达到这个目的。我们把每一个返回行附加到fileLines StringBuffer(带有一个换行符以保护行):String line = null; while((line=socketReader.readLine())!=null)

fileLines.append(line+"\n");

断开连接

这里我们实现tearDownConnection() 方法,它将在我们使用完毕连接后负责“清除”。tearDownConnection()方法只是分别关闭我们在Socket的InputStream和OutputStream 上创建的BufferedReader和PrintWriter。这样做会关闭我们从Socket获取的底层流,所以我们必须捕捉可能的IOException。

总结一下客户机

我们的类研究完了。在我们继续往前讨论服务器端的情况之前,让我们回顾一下创建和使用Socket 的步骤:

1. 用您想连接的机器的IP 地址和端口实例化Socket(如有问题则抛出Exception)。

2. 获取Socket 上的流以进行读写。

3. 把流包装进BufferedReader/PrintWriter 的实例,如果这样做能使事情更简单的话。

4. 对Socket 进行读写。

5. 关闭打开的流。

5 创建服务器Socket

创建RemoteFileServer 类

import java.io.*;

import https://www.doczj.com/doc/ac12474394.html,.*;

public class RemoteFileServer {

int listenPort;

public RemoteFileServer(int listenPort) {

this.listenPort=listenPort;

}

//允许客户机连接到服务器,等待客户机请求

public void acceptConnections() {

try {

ServerSocket server = new ServerSocket(listenPort);

Socket incomingConnection = null;

while(true) {

incomingConnection = server.accept();

handleConnection(incomingConnection);

}

}

catch(BindException e) {

System.out.println("Unable to bind to port "+listenPort);

}

catch(IOException e) {

System.out.println("Unable to instantiate a ServerSocket on port: "+listenPort);

}

}

//与客户机Socket交互以将客户机所请求的文件的内容发送到客户机

public void handleConnection(Socket incomingConnection) {

try {

OutputStream outputToSocket = incomingConnection.getOutputStream();

InputStream inputFromSocket = incomingConnection.getInputStream();

BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputFromSocket));

FileReader fileReader = new FileReader(new

File(streamReader.readLine()));

BufferedReader bufferedFileReader = new BufferedReader(fileReader);

PrintWriter streamWriter = new

PrintWriter(incomingConnection.getOutputStream());

String line = null;

while((line=bufferedFileReader.readLine())!=null){

streamWriter.println(line);

}

fileReader.close();

streamWriter.close();

streamReader.close();

}

catch(Exception e) {

System.out.println("Error handling a client: "+e);

e.printStackTrace();

}

}

public static void main(String args[]) {

RemoteFileServer server = new RemoteFileServer(1001);

server.acceptConnections();

}

}

跟客户机中一样,我们首先导入https://www.doczj.com/doc/ac12474394.html,的java.io。接着,我们给我们的类一个实例变量以保存端口,我们从该端口侦听进入的连接。缺省情况下,端口是1001。

我们的类有一个main()方法和两个其它方法。稍后我们将探究这些方法的细节。现在您只需知道acceptConnections()将允许客户机连接到服务器以及handleConnection()与客户机Socket交互以将您所请求的文件的内容发送到客户机。

实现main()

这里我们实现main()方法,它将创建RemoteFileServer并告诉它接受连接:服务器端的main()方法中,我们实例化一个新RemoteFileServer,它将在侦听端口(1001)上侦听进入的连接请求。然后我们调用acceptConnections()来告诉该server进行侦听。

接受连接

这里我们实现acceptConnections() 方法,它将创建一个ServerSocket 并等待连接请求:public void acceptConnections() {

try {

ServerSocket server = new ServerSocket(listenPort);

Socket incomingConnection = null;

while(true) {

incomingConnection = server.accept();

handleConnection(incomingConnection);

}

}

catch(BindException e) {

System.out.println("Unable to bind to port "+listenPort);

}

catch(IOException e) {

System.out.println("Unable to instantiate a ServerSocket on port:

"+listenPort);

}

}

acceptConnections()用欲侦听的端口号来创建ServerSocket。然后我们通过调用该ServerSocket的accept()来告诉它开始侦听。accept()方法将造成阻塞直到来了一个连接请求。此时,accept()返回一个新的Socket,这个Socket绑定到服务器上一个随机指定的端口,返回的Socket被传递给handleConnection()。请注意我们在一个无限循环中处理对连接的接受。这里不支持任何关机。

无论何时如果您创建了一个无法绑定到指定端口(可能是因为别的什么控制了该端口)的ServerSocket,Java代码都将抛出一个错误。所以这里我们必须捕捉可能的BindException。就跟在客户机端上时一样,我们必须捕捉IOException,当我们试图在ServerSocket上接受连接时,它就会被抛出。请注意,您可以通过用毫秒数调用setSoTimeout()来为accept()调用设置超时,以避免实际长时间的等待。调用setSoTimeout()将使accept()经过指定占用时间后抛出IOException。

处理连接

这里我们实现handleConnection()方法,它将用连接的流来接收输入和写输出:public void handleConnection(Socket incomingConnection) {

try {

OutputStream outputToSocket = incomingConnection.getOutputStream();

InputStream inputFromSocket = incomingConnection.getInputStream();

BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputFromSocket));

FileReader fileReader = new FileReader(new

File(streamReader.readLine()));

BufferedReader bufferedFileReader = new BufferedReader(fileReader);

PrintWriter streamWriter = new

PrintWriter(incomingConnection.getOutputStream());

String line = null;

while((line=bufferedFileReader.readLine())!=null){

streamWriter.println(line);

}

fileReader.close();

streamWriter.close();

streamReader.close();

}

catch(Exception e) {

System.out.println("Error handling a client: "+e);

e.printStackTrace();

}

}

跟在客户机中一样,我们用getOutputStream()和getInputStream()来获取与我们刚创建的Socket相关联的流。跟在客户机端一样,我们把InputStream包装进BufferedReader,把OutputStream包装进PrintWriter。在服务器端上,我们需要添加一些代码,用来读取目标文件和把内容逐行发送到客户机。这里是重要的代码:

FileReader fileReader = new FileReader(new File(streamReader.readLine())); BufferedReader bufferedFileReader = new BufferedReader(fileReader); String line = null; while((line=bufferedFileReader.readLine())!=null)

{ streamWriter.println(line); }

这些代码值得详细解释。让我们一点一点来看:

FileReader fileReader = new FileReader(new File(streamReader.readLine()));

首先,我们使用Socket 的InputStream的BufferedReader。我们应该获取一条有效的文件路径,所以我们用该路径名构造一个新File。我们创建一个新FileReader来处理读文件的操作。

BufferedReader bufferedFileReader = new BufferedReader(fileReader);

这里我们把FileReader包装进BufferedReader以使我们能够逐行地读该文件。

接着,我们调用BufferedReader的readLine()。这个调用将造成阻塞直到有字节到来。我们获取一些字节之后就把它们放到本地的line变量中,然后再写出到客户机上。完成读写操作之后,我们就关闭打开的流。

请注意我们在完成从Socket的读操作之后关闭streamWriter和streamReader。您或许会问我们为什么不在读取文件名之后立刻关闭streamReader。原因是当您这样做时,您的客户机将不会获取任何数据。如果您在关闭streamWriter之前关闭streamReader,则您可以往Socket写任何东西,但却没有任何数据能通过通道(通道被关闭了)。

总结一下服务器

在我们接着讨论另一个更实际的示例之前,让我们回顾一下创建和使用ServerSocket的步骤:

1. 用一个您想让它侦听传入客户机连接的端口来实例化一个ServerSocket(如有问题则抛出Exception)。

2. 调用ServerSocket的accept()以在等待连接期间造成阻塞。

3. 获取位于该底层Socket的流以进行读写操作。

4. 按使事情简单化的原则包装流。

5. 对Socket进行读写。

6. 关闭打开的流(并请记住,永远不要在关闭Writer之前关闭Reader)。

6 创建多线程Socket服务器

前面的示例教给您基础知识,但并不能令您更深入。如果您到此就停止了,那么您一次只能处理一台客户机。原因是handleConnection()是一个阻塞方法。只有当它完成了对当前连接的处理时,服务器才能接受另一个客户机。在多数时候,您将需要(也有必要)一个多线程服务器。

创建MultithreadedRemoteFileServer 类

import java.io.*;

import https://www.doczj.com/doc/ac12474394.html,.*;

public class MultithreadedRemoteFileServer {

int listenPort;

public MultithreadedRemoteFileServer(int listenPort) {

this.listenPort=listenPort;

}

//允许客户机连接到服务器,等待客户机请求

public void acceptConnections() {

try {

ServerSocket server = new ServerSocket(listenPort, 5);

Socket incomingConnection = null;

while(true) {

incomingConnection = server.accept();

handleConnection(incomingConnection);

}

}

catch(BindException e) {

System.out.println("Unable to bind to port "+listenPort);

}

catch(IOException e) {

System.out.println("Unable to instantiate a ServerSocket on port: "+listenPort);

}

}

//与客户机Socket交互以将客户机所请求的文件的内容发送到客户机

public void handleConnection(Socket connectionToHandle) {

new Thread(new ConnectionHandler(connectionToHandle)).start();

}

public static void main(String args[]) {

MultithreadedRemoteFileServer server = new MultithreadedRemoteFileServer(1001);

server.acceptConnections();

}

}

这里我们实现改动过acceptConnections()方法,它将创建一个能够处理待发请求的ServerSocket,并告诉ServerSocket接受连接。

新的server 仍然需要acceptConnections(),所以这些代码实际上是一样的。突出显示的行表示一个重大的不同。对这个多线程版,我们现在可以指定客户机请求的最大数目,这些请求都能在实例化ServerSocket期间处于待发状态。如果我们没有指定客户机请求的最大数目,则我们假设使用缺省值50。

这里是它的工作机制。假设我们指定待发数(backlog 值)是5并且有五台客户机请求连接到我们的服务器。我们的服务器将着手处理第一个连接,但处理该连接需要很长时间。由于我们的待发值是5,所以我们一次可以放五个请求到队列中。我们正在处理一个,所以这意味着还有其它五个正在等待。等待的和正在处理的一共有六个。当我们的服务器仍忙于接受一号连接(记住队列中还有2?6 号)时,如果有第七个客户机提出连接申请,那么,该第七个客户机将遭到拒绝。我们将在带有连接池服务器示例中说明如何限定能同时连接的客户机数目。

处理连接:

public void handleConnection(Socket connectionToHandle) {

new Thread(new ConnectionHandler(connectionToHandle)).start();

}

我们对RemoteFileServer所做的大改动就体现在这个方法上。我们仍然在服务器接受一个连接之后调用handleConnection(),但现在我们把该Socket传递给ConnectionHandler的一个实例,它是Runnable的。我们用ConnectionHandler创建一个新Thread 并启动它。ConnectionHandler的run()方法包Socket读/写和读File的代码,这些代码原来在RemoteFileServer的handleConnection()中。

创建ConnectionHandler 类

import java.io.*;

import https://www.doczj.com/doc/ac12474394.html,.*;

public class ConnectionHandler implements Runnable {

protected Socket socketToHandle;

public ConnectionHandler(Socket socketToHandle) {

this.socketToHandle=socketToHandle;

}

public void run() {

try {

PrintWriter streamWriter = new

PrintWriter(socketToHandle.getOutputStream());

BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream()));

String fileToRead = streamReader.readLine();

BufferedReader fileReader = new BufferedReader(new

FileReader(fileToRead));

String line =null;

while((line=fileReader.readLine())!=null) {

streamWriter.println(line);

}

fileReader.close();

streamWriter.close();

streamReader.close();

}

catch(Exception e) {

System.out.println("Error handling a client: "+e);

e.printStackTrace();

}

}

}

这个助手类相当简单。跟我们到目前为止的其它类一样,我们导入https://www.doczj.com/doc/ac12474394.html,和java.io。该

类只有一个实例变量socketToHandle,它保存由该实例处理的Socket。

类的构造器用一个Socket实例作参数并将它赋给socketToHandle。

请注意该类实现了Runnable接口。实现这个接口的类都必须实现run()方法。这里我们实现run()方法,它将攫取我们的连接的流,用它来读写该连接,并在任务完成之后关闭它。ConnectionHandler的run()方法所做的事情就是RemoteFileServer上的handleConnection()所做的事情。首先,我们把InputStream和OutputStream分别包装(用Socket的getOutputStream()和getInputStream())进BufferedReader和PrintWriter。然后我们用这些代码逐行地读目标文件:

PrintWriter streamWriter = new PrintWriter(socketToHandle.getOutputStream());

BufferedReader streamReader = new BufferedReader(new InputStreamReader(socketToHandle.getInputStream()));

String fileToRead = streamReader.readLine();

BufferedReader fileReader = new BufferedReader(new

FileReader(fileToRead));

String line =null;

while((line=fileReader.readLine())!=null) {

streamWriter.println(line);

}

请记住我们应该从客户机获取一条有效的文件路径,这样用该路径名构造一个新File,把它包装进FileReader以处理读文件的操作,然后把它包装进BufferedReader以让我们逐行地读该文件。我们while循环中调用BufferedReader上的readLine()直到不再有要读的行。请记注,对readLine()的调用将造成阻塞,直到有字节来到为止。我们获取一些字节之后就把它们放到本地的line变量中,然后写出到客户机上。完成读写操作之后,我们关闭打开的流。

总结一下多线程服务器

让我们回顾一下创建和使用“多线程版”的服务器的步骤:

1. 修改acceptConnections() 以用缺省为50(或任何您想要的大于1 的指定数字)实例化ServerSocket。

服务器高并发解决方案

服务器高并发解决方案 篇一:JAVA WEB高并发解决方案 java处理高并发高负载类网站中数据库的设计方法(java教程,java处理大量数据,java高负载数据)一:高并发高负载类网站关注点之数据库没错,首先是数据库,这是大多数应用所面临的首个SPOF。尤其是的应用,数据库的响应是首先要解决的。 一般来说MySQL是最常用的,可能最初是一个mysql 主机,当数据增加到100万以上,那么,MySQL的效能急剧下降。常用的优化措施是M-S(主-从)方式进行同步复制,将查询和操作和分别在不同的服务器上进行操作。我推荐的是M-M-Slaves方式,2个主Mysql,多个Slaves,需要注意的是,虽然有2个Master,但是同时只有1个是Active,我们可以在一定时候切换。之所以用2个M,是保证M不会又成为系统的SPOF。 Slaves可以进一步负载均衡,可以结合LVS,从而将select操作适当的平衡到不同的slaves上。 以上架构可以抗衡到一定量的负载,但是随着用户进一步增加,你的用户表数据超过1千万,这时那个M变成了SPOF。你不能任意扩充Slaves,否则复制同步的开销将直线上升,怎么办?我的方法是表分区,从业务层面上进行分区。最简单的,以用户数据为例。根据一定的切分方式,比如id,

切分到不同的数据库集群去。 全局数据库用于meta数据的查询。缺点是每次查询,会增加一次,比如你要查一个用户nightsailer,你首先要到全局数据库群找到nightsailer对应的cluster id,然后再到指定的cluster找到nightsailer的实际数据。 每个cluster可以用m-m方式,或者m-m-slaves方式。这是一个可以扩展的结构,随着负载的增加,你可以简单的增加新的mysql cluster进去。 需要注意的是: 1、禁用全部auto_increment的字段 2、id需要采用通用的算法集中分配 3、要具有比较好的方法来监控mysql主机的负载和服务的运行状态。如果你有30台以上的mysql数据库在跑就明白我的意思了。 4、不要使用持久性链接(不要用pconnect),相反,使用sqlrelay这种第三方的数据库链接池,或者干脆自己做,因为php4中mysql的链接池经常出问题。 二:高并发高负载网站的系统架构之HTML静态化 其实大家都知道,效率最高、消耗最小的就是纯静态化 /shtml/XX07/的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实

Java高并发,如何解决,什么方式解决

对于我们开发的网站,如果网站的访问量非常大的话,那么我们就需要考虑相关的并发访问问题了。而并发问题是绝大部分的程序员头疼的问题, 但话又说回来了,既然逃避不掉,那我们就坦然面对吧~今天就让我们一起来研究一下常见的并发和同步吧。 为了更好的理解并发和同步,我们需要先明白两个重要的概念:同步和异步 1、同步和异步的区别和联系 所谓同步,可以理解为在执行完一个函数或方法之后,一直等待系统返回值或消息,这时程序是出于阻塞的,只有接收到 返回的值或消息后才往下执行其它的命令。 异步,执行完函数或方法后,不必阻塞性地等待返回值或消息,只需要向系统委托一个异步过程,那么当系统接收到返回 值或消息时,系统会自动触发委托的异步过程,从而完成一个完整的流程。 同步在一定程度上可以看做是单线程,这个线程请求一个方法后就待这个方法给他回复,否则他不往下执行(死心眼)。 异步在一定程度上可以看做是多线程的(废话,一个线程怎么叫异步),请求一个方法后,就不管了,继续执行其他的方法。 同步就是一件事,一件事情一件事的做。 异步就是,做一件事情,不引响做其他事情。 例如:吃饭和说话,只能一件事一件事的来,因为只有一张嘴。 但吃饭和听音乐是异步的,因为,听音乐并不引响我们吃饭。 对于Java程序员而言,我们会经常听到同步关键字synchronized,假如这个同步的监视对象是类的话,那么如果当一个对象 访问类里面的同步方法的话,那么其它的对象如果想要继续访问类里面的这个同步方法的话,就会进入阻塞,只有等前一个对象 执行完该同步方法后当前对象才能够继续执行该方法。这就是同步。相反,如果方法前没有同步关键字修饰的话,那么不同的对象 可以在同一时间访问同一个方法,这就是异步。 在补充一下(脏数据和不可重复读的相关概念): 脏数据 脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这

高并发下的接口幂等性解决方案

高并发下的接口幂等性解决方案 我们实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果。例如: 1.前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果。 2.我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统 bug重发,也应该只扣一次钱; 3.发送消息,也应该只发一次,同样的短信发给用户,用户会哭的; 4.创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题。 等等很多重要的情况,这些逻辑都需要幂等的特性来支持。 二、幂等性概念 幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,“getUsername()和setTrue()”函数就是一个幂等函数.更复杂的操作幂等保证是利用唯一交易号(流水号)实现。我的理解:幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的 三、技术方案 1. 查询操作查询一次和查询多次,在数据不变的情况下,查询结果是一样的。select是天然的幂等操作; 2. 删除操作删除操作也是幂等的,删除一次和多次删除都是把数据删除。(注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个); 3.唯一索引,防止新增脏数据比如:支付宝的资金账户,支付宝也有用户账户,每个用户只能有一个资金账户,怎么防止给用户创建资金账户多个,那么给资金账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录。 要点:唯一索引或唯一组合索引来防止新增数据存在脏数据(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可) 4. token机制,防止页面重复提交 业务要求: 页面的数据只能被点击提交一次。发生原因:由于重复点击或者网络重发,或者nginx重发等情况会导致数据被重复提交 解决办法:集群环境:采用token加redis(redis单线程的,处理需要排队)单JVM环境:采用token加redis或token加jvm内存。 处理流程: 1.数据提交前要向服务的申请token,token放到redis或jvm内存,token有效时间 2.提交后后台校验token,同时删除token,生成新的token返回; 3.token特点:要申请,一次有效性,可以限流; 4.注意:redis要用删除操作来判断token,删除成功代表token校验通过,如果用select+delete 来校验token,存在并发问题,不建议使用; 5. 悲观锁获取数据的时候加锁获取select * from table_xxx where id=3939 for update;注意:id 字段一定是主键或者唯一索引,不然是锁表,会死人的悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,根据实际情况选用。 6. 乐观锁乐观锁只是在更新数据那一刻锁表,其他时间不锁表,所以相对于悲观锁,效率更高。乐观锁的实现方式多种多样可以通过version或者其他状态条件:

Java架构师必备知识点(高级程序员教程必备)

Java架构师必备知识点(高级程序员教程) 2019年3月 一、并发编程 1.线程安全: 当多个线程访问某一个类(对象)时这个类始终都能表现出正确的行为,那么这个类(对象和方法)就是线程安全的。 2.synchronized: 可以在任意对象以及方法上加锁,而加锁的这段代码称为"互斥区"或者"临界区"。 一个线程想要执行synchronized修饰的方法里的内容,首先是尝试获得锁,如果拿到锁,执行synchronized方法体里面的内容 如果拿不到那么这个线程会不断的尝试获得这把锁,直到拿到为止,而且是多个线程去竞争这把锁。 3.多个线程多个锁: 多个线程,每个线程都将可以拿到自己指定的锁,分别获得锁之后,执行synchronized方法体的内容, 关键字synchronized获得的锁都是对象锁,而不是把一段代码(方法)当做锁,在静态方法上机上synchronized获得的锁为类级别的锁,表示锁定类。 4.对象锁的同步和异步: 同步synchronized:同步就是共享,同步的目的是为了线程安全,对于线程安全需要满足两个特性:原子性(同步)、可见性。 异步asynchronized:异步就是独立,相互之间不受任何制约。 5.脏读:

对于对象的同步和异步方法,我们在设计程序的时候,一定要考虑问题的整体,不然就会出现数据不一致错误,很经典的错误就是脏读(dityread)。 在我们对一个对象的方法加锁的时候,需要考虑业务的整体性,即为setValue和getValue方法同时加锁synchronized同步关键字 保证(service)业务逻辑层的原子性,不然会出现业务逻辑错误。 6.synchronized锁重入: 关键字synchronized拥有重入锁的功能,也就是在使用synchronized时,当一个线程得到一个对象的锁后,再次请求此对象时是可以再次得到该对象的锁。 7.出现异常,锁自动释放: 对于web应用程序,异常释放锁的情况,如果不及时处理,很可能对应用程序业务逻辑产生严重的错误。 比如:现在执行一个队列任务,很多对象都去在等待一个对象正确执行完毕再释放锁, 但是一个对象出现由于异常的出现,导致业务逻辑没有正常执行完毕,就是释放了锁,那么后面的对象执行的都是错误的业务逻辑。 8.synchronized代码块: 使用synchronized声明的方法在某些情况下是有弊端的,比如:A线程方法调用一个时间很长的任务,那么B线程 必须等待很长的时间才能执行,这样的情况下可以使用synchronized代码块去优化代码执行时间,也就是减小锁的时间。 synchronized可以使用任何object对象进行加锁,用法比较灵活,不要使用string的常量加锁,会出现死循环问题。 9.锁对象改变的问题: 当使用一个对象进行加锁的时候,要注意对象本身发生改变的时候,那

.net高并发解决方案_1

竭诚为您提供优质文档/双击可除.net高并发解决方案 篇一:开源企业级web高并发解决方案 开源企业级web高并发解决方案 主要介绍利用开源的解决方案,来为企业搭建web高并发服务器架构花了一个多小时,画了张图片,希望能先帮你理解整个架构,之后我在一一介绍.linux的大型架构其实是一点点小架构拼接起来的,笔者从各个应用开始配置,最后在完全整合起来,以实现效果。 笔者所使用的环境为Rhel5.4内核版本2.6.18实现过程在虚拟机中,所用到的安装包为dVd光盘自带rpm包装过developmentlibrariesdevelopmenttools包组 笔者所使用的环境为Rhel5.4内核版本2.6.18实现过程在虚拟机中,所用到的安装包为dVd光盘自带rpm包装过developmentlibrariesdevelopmenttools包组 笔者虚拟机有限,只演示单边varnish配置 一、配置前端lVs负载均衡 笔者选用lVs的dR模型来实现集群架构,如果对dR模型不太了了解的朋友建议先去看看相关资料。

本模型实例图为: 现在director 上安装ipvsadm,笔者yum配置指向有集群源所以直接 用yum安装。yuminstallipvsadm 下面是director配置: dip配置在接口上172.16.100.10 Vip配置在接口别名上:172.16.100.1 varnish服务器配置:Rip配置在接口上:172.16.100.11;Vip配置在lo别名上 如果你要用到下面的heartbeat的ldirectord来实现 资源转换,则下面的#director配置不用配置 1.#director配置 2.ifconfigeth0172.16.100.10/16 3.ifconfigeth0:0172.16.100.1broadcast172.16.100.1ne tmask255.25 5.255.255up 4.routeadd-host172.16.100.1deveth0:0 5.echo1>/proc/sys/net/ipv4/ip_forward 1.#varnish服务器修改内核参数来禁止响应对Vip的aRp广播请求 2.echo1>/proc/sys/net/ipv4/conf/lo/arp_ignore

黑马程序员:高并发解决方案

黑马程序员:高并发解决方案 一、什么是高并发 高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。高并发相关常用的一些指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等。 响应时间:系统对请求做出响应的时间。例如系统处理一个HTTP请求需要200ms,这个200ms就是系统的响应时间。 吞吐量:单位时间内处理的请求数量。 QPS:每秒响应请求数。在互联网领域,这个指标和吞吐量区分的没有这么明显。并发用户数:同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。 二、什么是秒杀 秒杀场景一般会在电商网站举行一些活动或者节假日在12306网站上抢票时遇到。对于电商网站中一些稀缺或者特价商品,电商网站一般会在约定时间点对其进行限量销售,因为这些商品的特殊性,会吸引大量用户前来抢购,并且会在约定的时间点同时在秒杀页面进行抢购。

此种场景就是非常有特点的高并发场景,如果不对流量进行合理管控,肆意放任大流量冲击系统,那么将导致一系列的问题出现,比如一些可用的连接资源被耗尽、分布式缓存的容量被撑爆、数据库吞吐量降低,最终必然会导致系统产生雪崩效应。 一般来说,大型互联网站通常采用的做法是通过扩容、动静分离、缓存、服务降级及限流五种常规手段来保护系统的稳定运行。 三、扩容 由于单台服务器的处理能力有限,因此当一台服务器的处理能力接近或已超出其容量上限时,采用集群技术对服务器进行扩容,可以很好地提升系统整体的并行处理能力,在集群环境中,节点的数量越多,系统的并行能力和容错性就越强。在无状态服务下,扩容可能是迄今为止效果最明显的增加并发量的技巧之一。从扩容方式角度讲,分为垂直扩容(scale up)和水平扩容(scale out)。垂直扩容就是增加单机处理能力,怼硬件,但硬件能力毕竟还是有限;水平扩容说白了就是增加机器数量,怼机器,但随着机器数量的增加,单应用并发能力并不一定与其呈现线性关系,此时就可能需要进行应用服务化拆分了。 从数据角度讲,扩容可以分为无状态扩容和有状态扩容。无状态扩容一般就是指我们的应用服务器扩容;有状态扩容一般是指数据存储扩容,要么将一份数据拆分成不同的多份,即sharding,要么就整体复制n份,即副本。sharding遇

高并发网站系统架构解决方案

高并发网站系统架构解决方案 一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站所能比拟的。 大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。 上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。 1、HTML静态化 其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。 除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。 同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。 2、图片服务器分离

数据库高并发升级方案1

XXXXXXXXXXXX平台数据库升级方案 XXXXXXXXXXXXXXX有限公司2016年11月28日

目录 1. 概述 (4) 1.1. 背景 (4) 1.2. 目标与目的 (4) 1.3. 可行性分析 (4) 1.4. 参考依据 (5) 2. 数据库高并发方案 (5) 2.1. 数据库均衡负载(RAC) (5) 2.2. 数据库主从部署 (8) 2.3. 数据库垂直分割 (9) 2.4. 数据库水平分割 (10) 3. 二代办公平台数据库优化设计 (11) 3.1. 数据库集群 (11) 3.2. 重点业务表分区 (11) 3.3. 任务表历史数据分割 (12) 3.4. 数据库表结构优化 (12) 3.5. 数据访问优化 (12) 4. 实施方案 (13) 5. 工作量及预算评估 (14) 5.1. 工作量及预算评估 (14) 5.2. 其他费用 (15)

1.概述 1.1.背景 随着XXXXXX平台及其他子系统业务量增多,且用户已面向各地州市,用户数量增大,现有的二代办公平台及其他子系统在单一环境下的架构体系和数据库架构体系也无法高效的满足这样的场景。 当前XXXXXX平台及其子系统通过搭建多台WEB服务器和双机热备份的方式进行部署运行。虽已提高了整体效率,但对于部分的业务处理还是未解决。部分业务量并发处理多,业务关联多等因素,导致对数据库并发处理的业务量大,读写量大等也无法用双机热备份进行解决。 因此,在此背景下提高数据库访问效率,增大访问吞吐量等将成为二代办公平台及其子系统运行顺畅的关键因素。 1.2.目标与目的 目标:依托现有系统服务和设备环境,建立可扩容、高并发、高吞吐量的数据库架构体系。 目的:为缓解当前XXXXXX平台机器及其他子系统对数据库访问过大,造成的访问效率低下的问题,提升数据库访问效率和并发效率。对部分业务繁杂的表和访问进行优化设计,缓解因此造成的使用效率低下问题。 1.3.可行性分析 数据库性能分析:根据当前的数据库性能分析,当前硬件设备的提高也无法满足数据库性能的提升,因此应考虑数据库访问控制和数据访问方面进行优化。现有的数据库虽也实现双机热备份,但访问的效率未较大改善,因此应考虑各健全的数据库高并发访问方案。 数据库优化分析:当前的数据库采用的ORACLE数据库,同时,现有的均衡负载、读写分离、数据分割技术较为成熟,在对系统进行适当调整和优化的情况下,能保证系统的正常运行。

高并发高访问量网站的运维技术

高并发高访问量网站的运维技术 1. 前言 对于小型的网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,尤其对于大型网络来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、web服务器、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站所能比拟的。 大型网站,比如大型门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言,还有高性能的Web容器。以上几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,本文将论述从低成本、高性能和高扩张性的角度来考虑对高并发高负载网站的运行与维护技术。 2. HTML静态化技术 在站点流量很大的时候,为了提高系统性能,减短系统响应时间,最简单的方法其实也是最有效的方法就是把站点做成静态的,因为大家都知道效率最高、消耗最小的就是纯静态化的html 页面,所以我们应该尽可能使我们的网站上的页面采用静态页面来实现。然而静态页面在性能上虽然具有不少优势,但是,相对动态页面,其灵活性不够,扩展性不好,以后维护起来也比较麻烦。特别对于大量内容并且更新频繁的网站,我们无法全部手动去挨个实现页面静态化,那么我们一般可以采用设计信息发布系统CMS,先做好静态页面的模板,在通过信息发布系统从数据源读取数据,生成html代码块替换模板中的标签,然后生成静态文件。像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。 同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免

高并发解决方案总结

高并发解决方案总结 1.使用缓存 在绝大多数情况下,服务器的压力都会集中在数据库,减少数据库的访问次数,就可以减轻服务器的压力。所以,在高并发场景下,缓存的作用是至关重要的。 redis缓存数据库,它可以很好的在一定程度上解决一瞬间的并发量,redis之所以能解决高并发的原因是它可以直接访问内存,提高了访问效率,解决了数据库服务器压力。 使用缓存框架的时候,我们需要关心的就是什么时候创建缓存和缓存失效策略。 缓存的创建可以通过很多的方式进行创建,具体也需要根据自己的业务进行选择。例如,供应商平台的应用信息,应用上线后就进行缓存。需要注意的是,当我们修改或删除应用信息的时候,我们要考虑到同步更新该条缓存。 2.数据库优化 数据库优化是性能优化的最基础的一个环节,虽然提供了缓存技术,但是对数据库的优化还是一个需要认真的对待。数据库优化的方式很多,这里说下分表与分区。 ?分表 分表的适用场景 1. 一张表的查询速度已经慢到影响使用的时候。 2.当频繁插入或者联合查询时,速度变慢。

分表后数据都是存放在分表里,总表只是一个外壳,存取数据发生在一个一个的分表里面。分表的重点是存取数据时,如何提高数据库的并发能力。分表后,单表的并发能力提高了,磁盘I/O性能也提高了。 以安审日志服务的历史记录表为例: 表按年月拆分,格式为:表名+年+月,例如:TEST_202001、TEST_202002、、TEST_202003……然后可以 根据日期来查询。 ?分区 分区的适用场景 1. 一张表的查询速度已经慢到影响使用的时候。 2.表中的数据是分段的。 3.对数据的操作往往只涉及一部分数据,而不是 所有的数据。 分区是把一张表的数据分成N多个区块,这些区块可以在同一个磁盘上,也可以在不同的磁盘上。分区把存放数据的 文件分成了许多小块,分区后的表还是一张表,数据处理还是 由自己来完成。 3.分离数据库中活跃的数据 数据库的数据虽然很多,但是经常被访问的数据还是有限的, 因此可以将这些相对活跃的数据进行分离出来单独进行保存来提高 处理效率。其实前边使用redis缓存的思想就是一个很明显的分离数据库中活跃的数据示例,将应用经常使用的数据缓存在内存中。

高并发网站架构解决方案

一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站所能比拟的。 大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。 上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。 1、HTML静态化 其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。 除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。 同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。

java,高并发解决方案

java,高并发解决方案 篇一:Java高并发程序设计 Java 高并发程序设计 第一课前沿 ? 在计算密集型的领域中非常适用并行程序 ? 多核CPU的产生促使了并行程序的设计,摩尔定律的失效,芯片的性能不能提高,4GHz已经是当下最高的频率,从而产生多核CPU的出现。 ? 活锁:两个资源A、B,进程1和2 分别占用A和B 时发现不能工作,就同时释放掉,然后又同时分别占用B 和A 如此反复!出现活锁 ? 阻塞: 第二课Java并行程序基础 ? 线程是比进程更细粒度的执行单元,可以说是属于进程的一个子集 ? 7, Thread 类的run()方法实现于Runnable 接口,如果直接调用run()方法,不会启动新的线程,而是在当前线程下运

行,start() 方法会开启一个新的线程。 两种方式,1 重载run方法2,实现Ruanable接口 8, Thread.Interrupt() 是一种比较的让线程终止的方法优雅的方法,不会立刻停止掉该程序,根据程序的设计,而是根据该程序的设计,当执行完该程序的一次循环之后,在下次循环程序开始之前中断,保证该程序的数据完整性。9,try { Thread.sleep(2000); } catch (InterruptedException e) { // //设置中断状态,抛出异常后会清除中断标记位e.printStackTrace(); } 10,查看当前被挂起的进程命令jps 查看进程中具体线程的信息jstack 5880 11, ? 等待线程结束join(),其本质是判断线程是否还存活,如果一直存活那么通 知调用该线程的其他线程等待,如果结束有JVM 调用notifyAll()唤醒所 有等待该线程的线程。 f (millis == 0) { while (isAlive()) { wait(0); }

大型网站高并发架构与自动化运维实战

大型网站高并发架构与自动化运维实战 运维工程师解决的问题? 1、1000台服务器规模,JAVA和PHP混合环境,如何构建一套高效的从测试环境代码测试到正式环境的代码发布、回滚以及软件更新、配置变更的可实施的解决方案及规范流程制度? 2、电商秒杀:前10秒100万并发抢购,请设计个方案解决之? 3、6个机房,近1000台服务器如何设计一套所有账号统一管理的解决方案? 4、不考虑硬件资源及带宽,请设计一套可行的网站架构,解决大流量DDOS攻击问题,请分层逐一详细说明? 5、500台服务器规模,如何实现跨机房容灾,即一个机房宕机,其他机房可以最快接管提供服务 什么是运维工程师? 一个互联网产品的上线流程 1、首先公司管理层给出指导思想,PM定位市场需求(或copy成熟应用)进行调研、分析、最终给出详细设计。 2、架构师根据产品设计的需求,如pv大小预估、服务器规模、应用架构等因素完成网络规划,架构设计等(基本上对网络变动不大,除非大项目) 3、开发工程师将设计code实现出来、测试工程师对应用进行测试。 4、好,到运维工程师出马了,首先明确一点不是说前三步就与运维工作无关了,恰恰相反,前三步与运维关系很大:应用的前期架构设计、软/硬件资源评估申请采购、应用设计性能隐患及评估、IDC、服务性能\安全调优、服务器系统级优化(与特定应用有关)等都需运维全程参与,并主导整个应用上线项目;运维工程师负责产品服务器上架准备工作,服务器系统安装、网络、IP、通用工具集安装。运维工程师还需要对上线的应用系统架构是否合理、是否具备可扩展性、及安全隐患等因素负责,并负责最后将产品(程序)、网络、系统三者进行拼接并最优化的组合在一起,最终完成产品上线提供用户使用,并周而复使:需求->开发(升级)->测试->上线(性能、安全问题等之前预估外的问题随之慢慢就全出来了)在这里提一点:网站开发模式与传统软件开发完全不一样,网站一天开发上线1~5个升级版本是家常便饭,用户体验为王嘛,如果某个线上问题像M$ 需要1年解决,用户早跑光了;应用上线后,运维工作才刚开始,具体工作可能包括:升级版本上线工作、服务监控、应用状态统计、日常服务状态巡检、突发故障处理、服务日常变更调整、集群管理、服务性能评估优化、数据库管理优化、随着应用PV增减进行应用架构的伸缩、安全、运维开发。

高并发下的接口幂等性解决方案

我们实际系统中有很多操作,是不管做多少次,都应该产生一样的效果或返回一样的结果。 例如: 1.前端重复提交选中的数据,应该后台只产生对应这个数据的一个反应结果。 2.我们发起一笔付款请求,应该只扣用户账户一次钱,当遇到网络重发或系统 bug重发,也应该只扣一次钱; 3.发送消息,也应该只发一次,同样的短信发给用户,用户会哭的; 4.创建业务订单,一次业务请求只能创建一个,创建多个就会出大问题。 等等很多重要的情况,这些逻辑都需要幂等的特性来支持。 二、幂等性概念 幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中。 在编程中.一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。 这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。 例如,“getUsername()和setTrue()”函数就是一个幂等函数. 更复杂的操作幂等保证是利用唯一交易号(流水号)实现. 我的理解:幂等就是一个操作,不论执行多少次,产生的效果和返回的结果都是一样的 三、技术方案 1. 查询操作查询一次和查询多次,在数据不变的情况下,查询结果是一 样的。select是天然的幂等操作 2. 删除操作删除操作也是幂等的,删除一次和多次删除都是把数据删除。 (注意可能返回结果不一样,删除的数据不存在,返回0,删除的数据多条,返回结果多个) 3.唯一索引,防止新增脏数据比如:支付宝的资金账户,支付宝也有用户 账户,每个用户只能有一个资金账户,怎么防止给用户创建资金账户多个,那么给资金账户表中的用户ID加唯一索引,所以一个用户新增成功一个资金账户记录 要点:唯一索引或唯一组合索引来防止新增数据存在脏数据(当表存在唯一索引,并发时新增报错时,再查询一次就可以了,数据应该已经存在了,返回结果即可) 4. token机制,防止页面重复提交 业务要求: 页面的数据只能被点击提交一次。发生原因:由于重复点击或者网络重发,或者nginx重发等情况会导致数据被重复提交 解决办法:集群环境:采用token加redis(redis单线程的,处理需要排队)单JVM环境:采用token加redis或token加jvm内存 处理流程:

Linux服务器开发—2小时搞定高并发网络编程

Linux服务器开发——2小时搞定高并发网络编程 一、使用多线程处理高并发的弊端 多线程处理高并发是常用同时处理多个并发用户请求的方式,但线程数过多会增加系统的资源消耗(线程本身占用的资源+线程切换带来的系统开销),同时因硬件和软件的限制,操作系统支持的线程数有限,也抑制了系统的吞吐量。 动脑学院以一个餐饮为例,每一个人来就餐就是一个事件,他会先看一下菜单,然后点餐。就像一个网站会有很多的请求,要求服务器做一些事情。处理这些就餐事件的就需要我们的服务人员了。 在多线程处理的方式会是这样的: 一个人来就餐,一个服务员去服务,然后客人会看菜单,点菜。服务员将菜单给后厨。 二个人来就餐,二个服务员去服务…… 五个人来就餐,五个服务员去服务…… 这个就是多线程的处理方式,一个事件到来,就会有一个线程服务。很显然这种方式在人少的情况下会有很好的用户体验,每个客人都感觉自己是VIP,专人服务的。如果餐厅一直这样同一时间最多来5个客人,这家餐厅是可以很好的服务下去的。 来了一个好消息,因为这家店的服务好,吃饭的人多了起来。同一时间会来10个客人,老板很开心,但是只有5个服务员,这样就不能一对一服务了,有些客人就要没有人管了。老板就又请了5个服务员,现在好了,又能每个人都受V IP待遇了。 越来越多的人对这家餐厅满意,客源又多了,同时来吃饭的人到了20人,老板高兴不起来了,再请服务员吧,占地方不说,还要开工钱,再请人就攒不到钱了。怎么办呢?老板想了想,10个服务员对付20个客人也是能对付过来的,服务员勤快点就好了,伺候完一个客人马上伺候另外一个,还是来得及的。综合考虑了一下,老板决定就使用10个服务人员的线程池啦~~~ 但是这样有一个比较严重的缺点就是,如果正在接受服务员服务的客人点菜很慢,其他的客人可能就要等好长时间了。有些火爆脾气的客人可能就等不了走人了。 来,动脑学院直接上演示代码:

java高并发的解决方案

java 高并发的解决方案 对于我们开发的网站,如果网站的访问量非常大的话, 那么我们就需要考虑相关的并发访问问题了。而并发问题 是绝大部分的程序员头疼的问题 ! 下面是小编分享的,欢迎 大家阅读 ! 常用的,可能最初是一个 mysql 主机,当数据增加到 100 万以上,那么,MySQ 啲效能急剧下降。常用的优化措施是 M-S (主-从)方式进行同步复制,将查询和操作和分别在不 同的服务器上进行操作。我推荐的是 M-M-Slaves 方式, 2 个主 Mysql ,多个 Slaves ,需要注意的是,虽然有 Master ,但是同时只有 1 个是 Active ,我们可以在一定时 候切 换。之所以用2个M 是保证M 不会又成为系统的 SPOF 。 Slaves 可以进一步负载均衡,可以结合 LVS,从而将 以上架构可以抗衡到一定量的负载,但是随着用户进 步增加,你的用户表数据超过 1千万,这时那个 M 变成 了 SPOF 你不能任意扩充 Slaves ,否则复制同步的开销将 直线上升,怎么办 ?我的方法是表分区,从业务层面上进行 分区。最简单的,以用户数据为例。根据一定的切分方式, 比如 id ,切分到不同的数据库集群去。 java 高并发的解决方案】 般来说MySQl 是最 2个 select 操作适当的平衡到不同的 slaves 上。

全局数据库用于 meta 数据的查询。缺点是每次查询, 会增加一次,比如你要查一个用户 nightsailer, 你首先要 后再到指定的 cluster 找到 nightsailer 的实际数据 个 cluster 可以用 m-m 方式,或者 m-m-slaves 方式。 个可以扩展的结构,随着负载的增加,你可以简单的增 加新的 mysql cluster 进去。 网站HTML 静态化解决方案 当一个Servlet 资源请求到达WE 餌艮务 器之后我们会 填充指定的JSP 页面来响应请求: HTTP 请求---Web 服务器---Servlet-- 业务逻辑处理-- 访问数据 -- 填充 JSP-- 响应请求 HTML 静态化之后: HTTP 请求---Web 服务器---Servlet--HTML-- 响应请求 缓存、负载均衡、存储、队列 存集群,一般来说部署 10 台左右就差不多 (10g 内存池 ) 。 需要注意一点,千万不能用使用 swaP,最好关闭 linux 的swap 。 2. 负载均衡 / 加速。可能上面说缓存的时候,有人第 想的是页面静态化,所谓的静态 html ,我认为这是常识, 不属于要点了。页面的静态化随之带来的是静态服务的负 载均衡和加速。我认为 Lighttped+Squid 是最好的方式了。 到全局数据库群找到 nightsailer 对应的 cluster id ,然 这是 1. 缓存是另一个大问题,我一般用 memcachec 来 做缓

大型网站平台优化方案

1. 平台优化方案 大型网站,在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。 上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。 1.1. HTML静态化 由于效率最高、消耗最小的就是纯静态化的html页面,所以尽可能使网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,无法全部手动去挨个实现,于是出现了常见的信息发布系统CMS,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。 除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,如Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。 同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储在数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。

WEB应用中的高并发问题

WEB应用中的高并发问题 大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。这些解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,以下从平时的项目经验以及引用一些博客的思路来尝试解决高并发的情况。 0、首先需要关注数据库 没错,首先是数据库,这是大多数应用所面临的首个SPOF(单点故障)。尤其是Web2.0的应用,数据库的响应是首先要解决的。 可能最初是一台主机,当数据增加到100万以上,那么,数据库的效能急剧下降。常用的优化措施是M-S(主-从)方式进行同步复制,将查询和操作和分别在不同的服务器上进行操作。我推荐的是M-M-Slaves方式,2个主Master,多个Slaves,需要注意的是,虽然有2个Master,但是同时只有1个是Active,我们可以在一定时候切换。之所以用2个M,是保证M不会又成为系统的SPOF。 Slaves可以进一步负载均衡,可以结合LVS,从而将select操作适当的平衡到不同的slaves 上。 以上架构可以抗衡到一定量的负载,但是随着用户进一步增加,你的用户表数据超过1千万,这时那个M变成了SPOF。你不能任意扩充Slaves,否则复制同步的开销将直线上升,怎么办?我的方法是表分区,从业务层面上进行分区。最简单的,以用户数据为例。根据一定的切分方式,比如id,切分到不同的数据库集群去。

全局数据库用于meta数据的查询。缺点是每次查询,会增加一次,比如你要查一个用户nightsailer,你首先要到全局数据库群找到nightsailer对应的cluster id,然后再到指定的cluster找到nightsailer的实际数据。 每个cluster可以用m-m方式,或者m-m-slaves方式。这是一个可以扩展的结构,随着负载的增加,你可以简单的增加新的mysql cluster进去。 1、HTML静态化 其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。 除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。 同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,

相关主题
文本预览
相关文档 最新文档