Html5 Websocket while死循环的问题

websocket的原理,前台/后台的实现就不说了,现在说一个应用过程中的问题,首先来看几个问题的引出 。

1.  页面初始化

var Socket = new WebSocket(url, [protocol] );

这样会在服务端开启一个线程来相应websocket请求,高性能,速度快,多例.struts2action,每个请求就会开启一个线程来处理.

2. 服务端的 onOpen onClose onMessage方法再特定条件下回调用,看一下onMessage方法,客户端调用socket.send(message)时会调用onMesage,如果要保持长连接,简单的做法是在onMessage方法内用一个while死循环来做.

3.如上1所说,响应是多例的,那么如何广播消息到客户端呢?通常用一个staticmap来保存相应类,这样即使在同类不同对象的情况下,依然能访问到众多服务端处理类,可以实现广播了。

综合以上三点, 我现在要实现一个广播功能,也就是客户端和服务端用长连接while死循环的方式,如果有个糟糕的客户,在同一个页面不停的刷新刷新再刷新,会出现什么问题?

不停刷新,while循环不能退出,线程不断增加,严重影响服务端性能。

解决方法:

目前的解决办法是一种补偿机制, 简单的来说就是在服务端想客户端广播消息时,如果发生socket write Exception则在static map里移除发生异常的服务响应对象同时赋值为null,这样在下次广播室,无效的服务器响应异常就不存在了

onMessage
{
  while(true) {
   for item: map {
      try{
          item.sendMessage("hello!");
      } catch(exception ex) {
          map.remove(item);item=null;
       }
   }
  }
}

经过这样的代码,实际上并不能减轻后期服务器性能,原因在于对象的while循环未结束, gc不会销毁对象收集内存.

问题:对象的方法未执行结束,能否销毁对象? 经过测试,答案是不能的

先看一个例子:

public class ThreadTest implements Runnable{

	String name;
	public ThreadTest(){}
	public ThreadTest(String s) {
		name = s;
	}
	public void run() {
		int i = 1;
		while(true) {
			System.out.println(name + "->" +i++);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
//main方法:
package com.ftl;
public class TestMain {
	/**
	 * @param args
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		ThreadTest t1 = new ThreadTest("t1");;
		ThreadTest t2 = new ThreadTest("t2");
		ThreadTest t3 = new ThreadTest("t3");
		new Thread(t1).start();
		new Thread(t2).start();
		new Thread(t3).start();
		int i = 1;
		while(i < 20) {
			System.out.println("main->"+i++);
			if(i == 10) {
				t1 = null;
				t2 = null;
				t3 = null;
			}
			System.out.println(t1);
			System.out.println(t2);
			System.out.println(t3);
			Thread.sleep(1000);
		}
	}

}

可以看到 在代码里我们把t1 t2 t3 值为 null , 但是线程的while循环依然在执行 即使 t1:null t2:null t3:null ,这就说明对象即使=null, gc也不会立即销毁对象.
遇到这样的情况怎么办? 这样在websocket长连接还是会有性能影响
简单的办法就是给个while结束的条件,增加个标识.
看主要代码:

public class ThreadTest implements Runnable{

	String name;
	Boolean isRun = true;
	public ThreadTest(){}
	public ThreadTest(String s) {
		name = s;
	}
	public void run() {
		int i = 1;
		while(true && isRun) {
			System.out.println(name + "->" +i++);
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public void setIsRun(boolean isRun) {
		this.isRun = isRun;
	}
	
}
//增加isRun来判断是否该结束.
public static void main(String[] args) throws InterruptedException {
		ThreadTest t1 = new ThreadTest("t1");;
		ThreadTest t2 = new ThreadTest("t2");
		ThreadTest t3 = new ThreadTest("t3");
		new Thread(t1).start();
		new Thread(t2).start();
		new Thread(t3).start();
		int i = 1;
		while(i < 20) {
			System.out.println("main->"+i++);
			if(i == 10) {
				t1.setIsRun(false);
				t2.setIsRun(false);
				t3.setIsRun(false);
			}
			System.out.println(t1);
			System.out.println(t2);
			System.out.println(t3);
			Thread.sleep(1000);
		}
	}


可以看到,这样就成果结束对象的执行,当然gc会在适当的时候销毁对象,这个我们不用管了,想管也管不住 0~ !!~0

回到我们之前的话题,那段代码怎么改?

onMessage
{
  while(true) {
   for item: map {
      try{
         if(item.getIsRun() == true) {
            item.sendMessage("hello!");
         } else {
            //这个服务端websocket对象需要gc销毁了
            //直接break, 结束循环
            break;
         }
      } catch(exception ex) {
          item.setIsRun(false);
          map.remove(item)
       }
   }
  }
}

最后这段代码假象一下现在又10个服务器websocket对象在运行while循环,但是只有一个页面,原因是由于客户刷新了页面9次,但是真正有有意义并且保持长连接的socket只有一个,那么在sendMessage时,会发生异常,因为socket write失败,这时我们抓住异常,确定是哪个服务端websocket对象,然后map移除,isRun=false,这样在下次循环时,isRun=FALSE,那么则会break,while结束,onMessage结束,gc负责销毁。 经过实测,没问题。

有不对的地方请指出,谢谢!!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值