java socket连接池 实现

公司目前要求做一个能够并发负载很高的webservice,使用了市面流行的框架例如cxf,axis等效果都不理想,即利用jmeter压力测试时,tps浮动较大,分析原因为每次请求webservice接口都会建立一个socket,如果超过最大端口数,那么就要等待原来的socket释放才能新建socket,所以想到了用socket连接池。

即利用socket发送http请求,可以说是实现了http的长连接

本人也是第一次写socket的连接池,所以把代码发出来,希望有共同研究方向的同行能帮我指正不足之处。

1.池成员数据结构
package com.socket.pool;

import java.net.Socket;

public class SocketMember {
    //socket对象
    private Socket socket;
    //是否正被使用
    private boolean inUse=false;
    public Socket getSocket() {
        return socket;
    }
    public void setSocket(Socket socket) {
        this.socket = socket;
    }
    public boolean isInUse() {
        return inUse;
    }
    public void setInUse(boolean inUse) {
        this.inUse = inUse;
    }
    
}



2.socket连接池类
package com.socket.pool;

import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class SocketPool {
//host
private String host;
//port
private int port;
//初始化socket数
private int initSize=5;
//最大socket数
private int maxSize=5;
//socket对象容器
private List<SocketMember> socketContainer=new ArrayList<SocketMember>(initSize);

public SocketPool(String host,int port){
    this.host=host;
    this.port=port;
    buildPoolPart();
}
//给socket容器增加成员 一次增加initSize个成员
private List<SocketMember> buildPoolPart(){
    List<SocketMember> poolPart=new ArrayList<SocketMember>(initSize);
    SocketMember member=null;
    for(int i=0;i<initSize;i++){
        Socket socket=null;
        try {
            socket = new Socket(host, port);       
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        member=new SocketMember();
        member.setSocket(socket);
        poolPart.add(member);
    }
    if(poolPart.size()>0){
    socketContainer.addAll(poolPart);
    }else{
        try {
            throw new Exception("扩大池容量失败");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
    return poolPart;
}
//获取池中空闲的socket 已做线程安全处理
public SocketMember getMemberFromPool(){
    SocketMember member=null;
    //同步块获取对象
    synchronized (this) {
         for(int i=0;i<socketContainer.size();i++){
             SocketMember memberInPool=socketContainer.get(i);
             boolean inUse=memberInPool.isInUse();
             if(inUse==false){
                 memberInPool.setInUse(true);
                 member=memberInPool;
                 System.out.println("成功获取对象,在池中的位置为:"+i);
                 break;
             }
         }
         //pool中没有空余
         if(member==null){
             if(socketContainer.size()<maxSize){    
                 //扩大池容量
                 List<SocketMember> newPoolPart=buildPoolPart();
                 //从新扩大的部分拿出一个来用
                 member=newPoolPart.get(0);
                 member.setInUse(true);
                 System.out.println("成功扩大池容量,当前size为:"+socketContainer.size());
             }
         }
    }
     //如果超过最大容量 等待 递归
     if(member==null){
         try {
             Thread.sleep(1000);
         }
         catch (InterruptedException e) {
             e.printStackTrace();
         }
         member=getMemberFromPool();
     }
     return member;
     
 }

public int getInitSize() {
    return initSize;
}

public void setInitSize(int initSize) {
    this.initSize = initSize;
}

public int getMaxSize() {
    return maxSize;
}

public void setMaxSize(int maxSize) {
    this.maxSize = maxSize;
}
public List<SocketMember> getSocketContainer() {
    return socketContainer;
}
public String getHost() {
    return host;
}
public int getPort() {
    return port;
}



}


3.守护线程
package com.socket.pool;

import java.io.IOException;
import java.net.Socket;
import java.util.List;
/**
 * 保持池中的socket一直存活 每隔一段时间遍历池检查socket对象 如果已连接那么发送心跳包 如果已断开 那么重新建立连接
  * @ClassName: DaemonThread
  * @Description: TODO
  * @Copyright: Copyright (c) 2016 
  * @Company: 深圳市梦网科技股份有限公司
  * @author hxq
  * @date 2016-8-1 上午10:26:43
  * @version V1.0
 */
public class DaemonThread implements Runnable{
    private SocketPool pool;
    public DaemonThread(SocketPool pool){
        this.pool=pool;
    }
    public void run() {
        List<SocketMember> container=pool.getSocketContainer();
        while(true){
            //10秒检查一次    
            try {
                Thread.sleep(10000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            //遍历检查是否连接 
            for(int i=0;i<container.size();i++){
                SocketMember member=container.get(i);
                Socket socket=member.getSocket();
                //给此socket加锁
                synchronized (socket) {
                    if(!member.isInUse()){
                        
                        if(socket.isConnected()){
                            //如果连接发送心跳包
                            KeepAliveExcutor excutor=new KeepAliveExcutor(member);
                            excutor.request();
                            
                        }else{
                            //如果失败重新建立socket
                            try {
                                socket=new Socket(pool.getHost(), pool.getPort());
                                member.setSocket(socket);
                            }
                            catch (IOException e) {
                                e.printStackTrace();
                            }
                            System.out.println(socket.getLocalPort()+" 断线重连");
                        }
                    }
                }
                //System.out.println(socket.getLocalPort()+" 状态"+socket.isConnected());  
            }
        }
        
    }

}
/**
 * 发送心跳包的executor
  * @ClassName: KeepAliveExcutor
  * @Description: TODO
  * @Copyright: Copyright (c) 2016 
  * @Company: 深圳市梦网科技股份有限公司
  * @author hxq
  * @date 2016-8-16 下午02:16:32
  * @version V1.0
 */
class KeepAliveExcutor extends AbstractSocketExecutor{

    public KeepAliveExcutor(SocketMember socketMember) {
        super(socketMember);
    }
    
    public void request(){ 
        String request=SocketPoolUtil.getNowTimestamp();
        super.request(request, "utf-8");
    }
    
}




5.抽象的业务执行类 因为通过socket能发送http tcp等 所以写了一个抽象类封装公共的方法
package com.socket.pool;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;


public abstract class AbstractSocketExecutor {
    protected SocketMember socketMember=null;
    protected Socket socket=null;
    protected String host;
    protected int port;
    
    public AbstractSocketExecutor(SocketMember socketMember){
        this.socketMember=socketMember;
        if(socketMember!=null){
            this.socket=socketMember.getSocket();
        }
        host=socket.getInetAddress().getHostName();
        port=socket.getPort();
    }
    /**
     * 发送请求
      *
      * @param request
      * @param charset    
      *
      * @Description: TODO
     */
    protected void request(String request,String charset){
        OutputStream out=null;
        try {
            out=socket.getOutputStream();
            out.write(request.getBytes(charset));    
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 返回响应stream
      *
      * @return    
      *
      * @Description: TODO
     */
    protected InputStream getResStream(){
        InputStream in=null;
        try {
            in = socket.getInputStream();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        return in;
    }
    /**
     * 设置状态为未使用
      *    
      *
      * @Description: TODO
     */
    public void back(){
        //不能关闭流,否则socket会被关闭
/*        try {
            socket.getOutputStream().close();
            socket.getInputStream().close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }*/
        socketMember.setInUse(false);
    }
}



6.抓取html页面的executor
package com.socket.pool;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
 * 发送http请求 抓取html页面内容 写的并不好 尤其是判断结束的方式 勉强能用。。。
  * @ClassName: HTMLSocketExecutor
  * @Description: TODO
  * @Copyright: Copyright (c) 2016 
  * @Company: 深圳市梦网科技股份有限公司
  * @author hxq
  * @date 2016-8-16 下午02:02:47
  * @version V1.0
 */
public class HTMLSocketExecutor extends AbstractSocketExecutor {
    private String charset="UTF-8";
    public HTMLSocketExecutor(SocketMember socketMember) {
        super(socketMember);
    }
    
    public void setRequest(String path){
        StringBuilder requestStr=new StringBuilder();
        requestStr.append("GET ");
        requestStr.append(path);
        requestStr.append(" HTTP/1.1\r\n");
        requestStr.append("User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36\r\n");
        requestStr.append("Host: ");
        requestStr.append(super.host);
        if(port!=80){
            requestStr.append(":");
            requestStr.append(super.port);
        }
        requestStr.append("\r\n");
        requestStr.append("\r\n");
        super.request(requestStr.toString(), charset);
    }
    
    public String getResponse(){
        StringBuilder responseResult=new StringBuilder();
        BufferedReader socketReader=null;
        try {
            socketReader=new BufferedReader(new InputStreamReader(super.getResStream(), charset)) ;
            String line=null;
            char[] body=null;
            int contentLength=-1;
            boolean isHeadEnd=false;
            while((line=socketReader.readLine())!=null){
                //System.out.println(line);
                responseResult.append(line);
                responseResult.append("\r\n");
                //检查请求头是否结束
                if(!isHeadEnd){
                    isHeadEnd=isBlankLine(line);
                    if(isHeadEnd){
                        //立即判断是否为请求头结束 如果是且有content-length 那么立即一次读取body 结束循环
                        if(contentLength!=-1){
                            if(contentLength==0){
                                break;
                            }
                            //如果content-length>0 那么一次性读出响应体   
                            body=new char[contentLength];
                            int bodyReadEndFlag=socketReader.read(body);
                            responseResult.append(body);
                            if(bodyReadEndFlag==-1){
                                break;
                            }
                            
                        }
                    }
                    if(contentLength==-1){
                        //没有赋值 检查content-length并赋值
                        contentLength=getContentLength(line);
                    }
                }else{
                    //已经读过了head结束行
                    if(isHTMLEnd(line)){
                        //如果碰到</html>
                        break;
                    }
                }

            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }finally{
            super.back();
        }
        
        return responseResult.toString();

    }
    
    private int getContentLength(String line){
        int result=-1;
        if(line.contains("Content-Length")){
            int splitIndex=line.indexOf(":");
            String length=line.substring(splitIndex+1,line.length());
            length=length.trim();
            result=Integer.valueOf(length);
        }
        return result;
    }
    
    private boolean isBlankLine(String line){
        boolean result=false;
        if("".equals(line)){
            result=true;
        }
        return result;
    }
    
    private boolean isHTMLEnd(String line){
        boolean result=false;
        if(line.indexOf("</html>")>-1){
            result=true;
        }
        return result;
    }


}


7.工厂类 用于组装exector对象 实例化executor对象 并将socket对象注入进去 (没有抽象,只能服务HTMLSocketExecutor 。。。 先凑合着用)
package com.socket.pool;


public class SocketExcutorFactory {
/*    private SocketExcutorFactory(){}
    private final static SocketExcutorFactory instance=new SocketExcutorFactory();
    public static SocketExcutorFactory getInstance(){
        return instance;
    }*/
    
    private SocketPool pool=null;
    public SocketExcutorFactory(String host,int port,boolean daemonFlag){
        pool=new SocketPool(host, port);
        if(daemonFlag){
        //守护线程
        Thread daemonThread=new Thread(new DaemonThread(pool));
        daemonThread.start();
        }
    }
    public SocketExcutorFactory(String host,int port){
        this(host, port, true);
    }
    /**
     * 实例化executor 并从池中拿出一个socket注进去
      *
      * @return    
      *
      * @Description: TODO
     */
    public HTMLSocketExecutor getHttpExecutor(){
        HTMLSocketExecutor executor=null;
        executor=new HTMLSocketExecutor(pool.getMemberFromPool());
        return executor;
    }
    

}




单线程测试类
package com.socket.pool;

public class TestPool {
public static void main(String[] args) {
String host="www.cnblogs.com";
int port=80;
SocketExcutorFactory factory=new SocketExcutorFactory(host, port,false);
HTMLSocketExecutor executor=factory.getHttpExecutor();
executor.setRequest("/zhangweizhong/p/5772330.html");
String res=executor.getResponse();
System.out.println(res);
}
}



多线程测试
package com.socket.pool;

public class ExcutorThread implements Runnable {
    HTMLSocketExecutor excutor=null;
    private static SocketExcutorFactory factory=new SocketExcutorFactory("www.baidu.com", 80);
    public ExcutorThread(){
        excutor=factory.getHttpExecutor();
    }
    public void run() {
        excutor.setRequest("/");
        String res=excutor.getResponse();
        if(res.contains("302 Found")){
            System.out.println(Thread.currentThread().getName()+" success");
        }else{
            System.out.println(Thread.currentThread().getName()+" fail");
        }

    }

public static void main(String[] args) {
    Thread thread=null;
    for(int i=0;i<10;i++){
      thread=new Thread(new ExcutorThread());
      thread.start();
  }
}

}


源码:

猜你喜欢

转载自516100981.iteye.com/blog/2314314
今日推荐