高手进来讨论一下,这段简单Server端代码的性能问题?要连几千个Client端!!!

liuzhengkang 2008-12-11 11:13:22
先说明一下:在这个问题里,大家可以看成是简单的Server/Client模型,一个服务端ServerSocket和多个客户端Socket的连接。

Client端是Delphi做的,程序上线之后可能会有几千个Client端,呵呵,如果可能的话,会上到几万个Client端。

这个是固定的:Client端每秒3次到Server端去取数据,每个Client端都是每秒3次取数。只有Client端程序退出之后Server才会断开这个Client端。

Server端的实现:

/**
*
*接收Client数据的服务线程
*
*/
class ClientServer extends Thread{
private ServerSocket ss;
private int PORT;
private DataSCalcServer dscs;

public ClientServer(int port,DataSCalcServer dscs){
this.PORT = port;
this.dscs = dscs;
}

public void run(){
ExecutorService execPool = null;
try{
ss = new ServerSocket(PORT);
// execPool = Executors.newFixedThreadPool(25); //创建固定大小的线程池* A处
while(true){
Socket socket = ss.accept();
// execPool.execute(new clientThread(socket));
new clientThread(socket).start();
}
}catch(IOException ioe){
execPool.shutdown();
System.out.println("Error ss.accept():"+ioe.getMessage());
}
}

class clientThread extends Thread{
private PrintWriter pw ;
private BufferedReader br ;
private Socket socket;

public clientThread(Socket socket){
this.socket = socket;
try{
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
pw = new PrintWriter(socket.getOutputStream());
}catch(Exception e){
e.printStackTrace();
}
}

public void run(){
boolean status = true;
try{
while(status){
try{
if(br != null){
String str = br.readLine().trim();
String packet = dscs.setToClientPacket(str); //打包发送给Client
if(packet != null){
pw.println(packet);
pw.flush();
}
}
sleep(1); //B处
}catch(Exception e){
status = false;
System.out.println("Warning delphiClient interrupted ....");
e.printStackTrace();
}
}
}catch(Exception e){
System.out.println("Error run():"+e.getMessage());
}finally{
try{
if(br != null)
br.close();
if(pw != null)
pw.close();
if(socket != null)
socket.close();
}catch(Exception e){
System.out.println("Error close failed.");
}
}
}
}
}


现在担心的问题就是:
1、如果几千个Client端,那Server端就会产生几千个线程,这样程序会不会死掉?! 以前测试过线程,就是当线程达到2000多个的时候,程序就会变慢了!
我想这个Server有几千个Client端时,也应该会死掉! 那我应该怎么改啊?!
2、如果上面A处使用线程池的话, Executors.newFixedThreadPool(25);这里线程池的大小要多少才好?! 呵呵!加了线程池的话,程序应该死不了了吧!死不了,但会有什么影响呢?!!
3、在B处加了睡眠之后,性能是不是应该有很大提升吧?!
...全文
445 51 打赏 收藏 转发到动态 举报
写回复
用AI写文章
51 条回复
切换为时间正序
请发表友善的回复…
发表回复
liuzhengkang 2008-12-23
  • 打赏
  • 举报
回复
[Quote=引用 45 楼 skylovers 的回复:]
引用 42 楼 liuzhengkang 的回复:

非常感谢hbwhwang!!

您说的很对!现在这种情况看起来还是用NIO好。我现在也很矛盾啊!这已经开发了几个星期了,要是全改为NIO,就算是时

间来的急,也担心后面的性能调试不知会花多少时间。

我想再问一下,如果我把这程序连接的Client限制到1000个左右,以后启动 N台服务器,这样应该可以吧?




这样估计就需要N+1台服务器了,1台调度连接,N台处理响应.有时1台都不够调…
[/Quote]
呵呵!顶了!!!
hbwhwang 2008-12-23
  • 打赏
  • 举报
回复
[Quote=引用 42 楼 liuzhengkang 的回复:]

非常感谢hbwhwang!!

您说的很对!现在这种情况看起来还是用NIO好。我现在也很矛盾啊!这已经开发了几个星期了,要是全改为NIO,就算是时

间来的急,也担心后面的性能调试不知会花多少时间。

我想再问一下,如果我把这程序连接的Client限制到1000个左右,以后启动 N台服务器,这样应该可以吧?
[/Quote]

启动N台服务器,你就要考虑负载均衡的问题了。
如果服务器访问需要登录,那你还得考虑会话保持的问题。
kbyst 2008-12-23
  • 打赏
  • 举报
回复
大家都很强啊

学习一下
oldwolf1987 2008-12-22
  • 打赏
  • 举报
回复
学习了
PostX 2008-12-22
  • 打赏
  • 举报
回复
难怪你玩不转,有没有用过mina消息框架呀.
轻松达到上K个客户端没有任何问题.
PostX 2008-12-22
  • 打赏
  • 举报
回复
难怪你玩不转,有没有用过mina消息框架呀.
轻松达到上K个客户端没有任何问题.
skylovers 2008-12-22
  • 打赏
  • 举报
回复
[Quote=引用 42 楼 liuzhengkang 的回复:]

非常感谢hbwhwang!!

您说的很对!现在这种情况看起来还是用NIO好。我现在也很矛盾啊!这已经开发了几个星期了,要是全改为NIO,就算是时

间来的急,也担心后面的性能调试不知会花多少时间。

我想再问一下,如果我把这程序连接的Client限制到1000个左右,以后启动 N台服务器,这样应该可以吧?

[/Quote]


这样估计就需要N+1台服务器了,1台调度连接,N台处理响应.有时1台都不够调度连接.不过用户几万应该是没什么问题的.

如果1秒3次的话,还需要考虑你的处理逻辑的复杂度了,1000个连接每秒就需要3000次处理.这个数据量还是不小的.如果对时间要求比较严格会更麻烦.毕竟就算使用NIO,你线程池也不可能放1000个在里面待命.换句话,1000个连接和1000个并发对系统的要求差距是很大的.

嘿嘿,网游服务器不好写吧~看你的需求就知道,不是网游就是股票,网游的可能性更大...
hbwhwang 2008-12-21
  • 打赏
  • 举报
回复
另外,如果你要用NIO,可以考虑使用Apache MINA--一个对non-blocking socket封装的开源框架
http://mina.apache.org/index.html
hbwhwang 2008-12-21
  • 打赏
  • 举报
回复
给你写了个非堵塞的例子,顺便温习了一下NIO.
服务器代码:


import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;

/**
* 非堵塞式 Server Socket 示例
* @author hbwhwang
*
*/
public class NonBlockServer extends Thread {
static final int PORT = 19001;
private ByteBuffer dataBuffer = ByteBuffer.allocate(1024);
private Selector selector = null;
private Charset charset = Charset.forName("GB2312");

public NonBlockServer() {
try {
startup();
} catch (IOException e) {
e.printStackTrace();
}
}

public void startup() throws IOException {
selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ServerSocket ss = ssc.socket();
ss.bind(new InetSocketAddress(PORT));

ssc.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server startup...");
}

/**
* 业务处理方法,这里只简单地返回系统时间
* @return
*/
public String bizMethod(){
return String.valueOf(System.nanoTime());
}

public void run() {
if (selector==null)
return;

while (true) {
try {
int num = selector.select();
if (num==0){
Thread.yield();
continue;
}
} catch (IOException e) {
e.printStackTrace();
}

Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();

if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc=null;
try {
sc = ssc.accept();
sc.configureBlocking(false);
// Add the new connection to the selector
sc.register(selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}

System.out.println("Got connection from "
+ sc.socket().getInetAddress() + ":"
+ sc.socket().getPort());
} else if (key.isReadable()) {
// Read the data
SocketChannel sc = (SocketChannel) key.channel();

dataBuffer.clear();
try {
//这里未对读入的信息处理。在实际环境中,可能读到的是指令。另外也可能一次读不完,那么需要考虑多次读
int r = sc.read(dataBuffer);
sc.register(selector, SelectionKey.OP_WRITE);
} catch (IOException e) {
e.printStackTrace();
}
dataBuffer.flip();
CharBuffer cb=charset.decode(dataBuffer);
String temp=cb.toString();
System.out.println("Read data from "
+ sc.socket().getInetAddress() + ":"
+ sc.socket().getPort() + " - "
+ temp+",length:"+temp.length());
} else if (key.isWritable()) {
dataBuffer.clear();
//这里没有考虑一次写不完的情况。。。
dataBuffer.put(bizMethod().getBytes());
dataBuffer.put("\n".getBytes());
dataBuffer.flip();
SocketChannel sc = (SocketChannel) key.channel();
try {
sc.write(dataBuffer);
sc.register(selector, 0);
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Write to ..."
+ sc.socket().getInetAddress() + ":"
+ sc.socket().getPort());
}
}
}
}

public static void main(String[] args) throws IOException {
new NonBlockServer().start();
}
}


测试客户端代码:

import java.net.*;
import java.io.*;

public class JabberClient extends Thread {
public void run(){
try {
InetAddress addr;
addr = InetAddress.getByName("localhost");
Socket socket = new Socket(addr, NonBlockServer.PORT);
BufferedReader in = new BufferedReader(new InputStreamReader(socket
.getInputStream()));
// Output is automatically flushed
// by PrintWriter:
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())), true);
out.print("howdy");
out.flush();
String str = in.readLine();
socket.close();
System.out.println(str);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
for (int i=0;i<10;i++){
new JabberClient().start();
}
}
} // /:~
jhr924 2008-12-21
  • 打赏
  • 举报
回复
帮顶了,
还没到回复楼主问题的境界。
希望楼主找到合适的解决方案。
CJljfn 2008-12-21
  • 打赏
  • 举报
回复
学习了!
liuzhengkang 2008-12-21
  • 打赏
  • 举报
回复
[Quote=引用 39 楼 hbwhwang 的回复:]
并发和线程的确是2码事
如果1秒钟内启动了1000个线程,并发可能是1000,也可能是1。如果每个线程要1秒才能完成,那么就是1000个并发,如果每个线程只要 <1/1000秒,那么就有可能顺序完成,就等于没有并发的线程。
用JAVA做的程序,超过1000个并发线程,机器的性能就会大打折扣,这是因为把大量的CPU时间耗费到线程的调度上了。
你提到的需求:“Client端每秒3次到Server端去取数据,每个Client端都是每秒3次取数。只有Clie…
[/Quote]

非常感谢hbwhwang!!

您说的很对!现在这种情况看起来还是用NIO好。我现在也很矛盾啊!这已经开发了几个星期了,要是全改为NIO,就算是时

间来的急,也担心后面的性能调试不知会花多少时间。

我想再问一下,如果我把这程序连接的Client限制到1000个左右,以后启动 N台服务器,这样应该可以吧?

hbwhwang 2008-12-20
  • 打赏
  • 举报
回复
并发和线程的确是2码事
如果1秒钟内启动了1000个线程,并发可能是1000,也可能是1。如果每个线程要1秒才能完成,那么就是1000个并发,如果每个线程只要<1/1000秒,那么就有可能顺序完成,就等于没有并发的线程。
用JAVA做的程序,超过1000个并发线程,机器的性能就会大打折扣,这是因为把大量的CPU时间耗费到线程的调度上了。
你提到的需求:“Client端每秒3次到Server端去取数据,每个Client端都是每秒3次取数。只有Client端程序退出之后Server才会断开这个Client端。 ”“如果可能的话,会上到几万个Client端。”
用你写的程序来处理这个需求,极可能达到1000以上的并发线程,因此可以说你的程序不是这种需求好的解决方案。
针对你的需求,采用异步非堵塞式IO是更好的方案。
同步IO采用的是轮询方式,直到读出或者写入数据,因此你要处理多个通道的数据,就需要多个线程。
而异步IO采用的是观察者模式,只有事件发生的时候,才运行程序。因此不需要多余的线程。
Friend_NO1 2008-12-20
  • 打赏
  • 举报
回复
顶一下!
liuzhengkang 2008-12-20
  • 打赏
  • 举报
回复
[Quote=引用 36 楼 hbwhwang 的回复:]
这么大的并发线程不好!
还是NIO吧
[/Quote]


谢谢大家的意见,本贴再加分!

大家好像都看成是“几千个并发”了,我都搞晕了,“大量连接”和“大量并发”!!?

几千个Client端连接,不一定就是几千个并发吧!!!

以上大家的观点都是NIO,我不知道那个好,因为我没有用过NIO,希望有用过的朋友给点意见!
hbwhwang 2008-12-20
  • 打赏
  • 举报
回复
这么大的并发线程不好!
还是NIO吧
歪嘴鱼 2008-12-19
  • 打赏
  • 举报
回复
try google "mina java"
liuzhengkang 2008-12-19
  • 打赏
  • 举报
回复
[Quote=引用 30 楼 fastmask 的回复:]
引用 29 楼 liuzhengkang 的回复:

引用 28 楼 fuyou001 的回复:
为什么楼主没有用非阻塞流


那个项目经理说不要用“非阻塞流”,这个好像是对每个客户不稳定吧?!我也不知道是什么原因!



你这种情况,非阻塞流的方式肯定优于阻塞方式的,况且你启动太多线程系统基本上调度都有问题。
对每个客户不稳定,这个不确切,那是在你的通讯策略方面可以调整的,并且你可以采用一组服务器来做接入,在
接入这里进行均衡。
[/Quote]



再次谢谢大家的回复!!

呵呵!再请问一下,非阻塞流可以保证实时性吗?刚找了这方面的资料,还是不太理解,但应该不行吧。

这个程序有关系到:即时信息通信的和交易方面的,就是对实时性要求比较高吧,

客户端发来请求的时候就要马上响应,就像我们网上购物下单什么的,实时响应速度一定要有保证啊。

这样的话NIO就不行了!

Danny 2008-12-19
  • 打赏
  • 举报
回复
关注下
skylovers 2008-12-19
  • 打赏
  • 举报
回复
NIO的响应速度不是问题,因为将套接字的轮询交由系统处理,理论上来说比线程监控Socket要快很多.这也是NIO的原始出发点.至于稳定性,TCP都不稳定了,那还要相信什么?

NIO+大容量线程池基本能确保非大并发的情况了,话说回来,如果是大并发,那用什么都没戏,只有Cluster.
加载更多回复(30)

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧