zoukankan      html  css  js  c++  java
  • HTTP轮询模型

    HTTP轮询模型

    长短轮询

    http协议是一种client-server模型的应用层协议,这种c-s的模式虽然大多数情况都能满足需求,但是某些场景也需要服务端能够将一些信息实时的推送到客户端,即实现服务器向客户端推消息的功能。

    比如:

    • 配置管理中心服务端需要将更新的配置推送到client端
    • 消息推送服务器需要将一些消息推送到Android、iOS客户端

    利用Http协议实现服务器推送有两种常见的思路:

    1. 短轮询拉

      客户端不停的去向服务器发送轮询请求,如果有数据更新,客户端能也能尽快(取决于轮询间隔)获取最新的数据,这种方式被称为Http短轮询。

      这里写图片描述

      这种方式有如下缺点:

      • 因为是短轮询,因此一定时间t内需要进行轮询的次数就更多,而Http的连接是需要tcp三次握手等资源开销的。
      • 由图可以看出,但是服务端数据发生更新时,客户端并不是立刻收到更新的数据(除去网络传输仍然还需要时间),而只能是在下一次轮询的时候才能感知到数据的变更。
    2. 长轮询推

    长轮询的思路是这样的:尽量减少轮询的次数,从而减少资源开销。为了减少轮询次数,那么每次轮询的时间跨度就需要比较长,因此成为长轮询,同时也希望长轮询模型的每一次轮询效率要高于短轮询。

    这里写图片描述

    长轮询模型有这么几个特征:

    • 每次轮询的间隔不固定
    • 服务器对每次轮询做出响应的条件是:超时或者数据更新
    • 长轮询模型中,客户端能实时感知到服务器端数据更新

    由于很多服务器都具有异步处理连接的能力,因此图中的阻塞消耗的资源比较小。

    异步Servlet

    下面是利用Servlet规范中提供的异步Servlet作为服务端的Http长轮询模型,实现了客户端能实时获取服务端某个配置文件内容。

    异步Servlet是Servlet3.0出来的新特性,对于需要异步处理的连接,Servlet引擎会将处理该请求的工作线程回收进工作线程池,而不是阻塞在该请求上。

    
    package httplongconnection;
    
    import java.io.File;
    import java.io.FileFilter;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintWriter;
    import javax.servlet.AsyncContext;
    import javax.servlet.AsyncEvent;
    import javax.servlet.AsyncListener;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.commons.io.monitor.FileAlterationListener;
    import org.apache.commons.io.monitor.FileAlterationMonitor;
    import org.apache.commons.io.monitor.FileAlterationObserver;
    
    @WebServlet(urlPatterns = "/long", asyncSupported = true)
    public class HttpLongConnectionServlet extends HttpServlet {
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        static FileAlterationObserver observer;
        static {
    
            FileAlterationMonitor monitor = new FileAlterationMonitor(1000L);// 每隔1000毫秒扫描一次
            // 需要监听的文件目录
            observer = new FileAlterationObserver(new File("E:/J2EE_workspace/httplongconnection/src/main/resources"), new FileFilter() {
    
                public boolean accept(File pathname) {
                    // TODO Auto-generated method stub
                    return true;
                }
            });
            System.out.println("observer");
            monitor.addObserver(observer);
            try {
                monitor.start();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            // TODO Auto-generated method stub
            final AsyncContext ctx = req.startAsync();
            ctx.addListener(new AsyncListener() {
    
                public void onTimeout(AsyncEvent event) throws IOException {
                    // TODO Auto-generated method stub
                    ctx.complete();
                    observer.removeListener((FileAlterationListener)ctx.getRequest().getAttribute("fileListener"));
                }
    
                public void onStartAsync(AsyncEvent event) throws IOException {
                    // TODO Auto-generated method stub
    
                }
    
                public void onError(AsyncEvent event) throws IOException {
                    // TODO Auto-generated method stub
    
                }
    
                public void onComplete(AsyncEvent event) throws IOException {
                    // TODO Auto-generated method stub
                    observer.removeListener((FileAlterationListener)ctx.getRequest().getAttribute("fuck"));
                }
            });
            ctx.setTimeout(50 * 1000);
            new Thread(new BizProcessor(ctx)).start();
        }
    
        class BizProcessor implements Runnable {
            private String checkSum;
            private AsyncContext asyncContext;
    
            private boolean checkSumEqual(InputStream is, String originalCheckSum) {
                try {
                    String digest = DigestUtils.md5Hex(is);
                    if (digest.equals(originalCheckSum)) {
                        return true;
                    }
                    this.checkSum = digest;
                    System.out.println(
                            "File has changed. new md5 is " + this.checkSum + ", old checsum is " + originalCheckSum);
                    return false;
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                return false;
            }
    
            public BizProcessor(AsyncContext asyncContext) {
                super();
                this.asyncContext = asyncContext;
            }
    
            public void run() {
                // TODO Auto-generated method stub
                // sleep
                HttpServletRequest req = (HttpServletRequest) asyncContext.getRequest();
                String cSum = String.valueOf(req.getParameter("checkSum"));
                // 文件update
                InputStream is = null;
                try {
                    is = new FileInputStream(new File("E:/J2EE_workspace/httplongconnection/src/main/resources/config.txt"));
                } catch (FileNotFoundException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                if (is != null && !checkSumEqual(is, cSum)) {
                    try {
                        is.close();
                        is = this.getClass().getClassLoader().getResourceAsStream("config.txt");
                        PrintWriter out = asyncContext.getResponse().getWriter();
                        String content = org.apache.commons.io.IOUtils.toString(is, "UTF-8");
                        System.out.println(content);
                        out.write(checkSum + "02" + content);
                        out.flush();
                        out.close();
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    asyncContext.complete();
                }
                System.out.println("register");
                register(asyncContext);
                // 没有发生文件更新,则等待超时发生
            }
    
            private void register(AsyncContext ctx) {
                // TODO Auto-generated method stub
                FileListerAdapter listner = new FileListerAdapter(ctx);
                ctx.getRequest().setAttribute("fileListener", listner);
                observer.addListener(listner);
            }
        }
    }
    
    
    <html>
    <script src="http://code.jquery.com/jquery-latest.js"></script>
    <script src="md5.js"></script>
    <body>
    <h2>Hello World!</h2>
    <textarea id="show" rows="40" cols="80">
    
    </textarea>
    </body>
    <script type="text/javascript">
      var cSum = "0";
      poll();
      function poll() {
          $.ajax({ 
                url: "/httplongconnection/long", 
                data : {"checkSum" : cSum},
                success: function(response) {
                    if (response == null || response.length == 0) {
                        poll();
                        return;
                    }
                    var msg = response.split("02");
                    var checkSum = md5(msg[1]), content = msg[1];
                    if (checkSum != cSum) {
                        cSum = checkSum;
                        $("#show").val(content);
                    }
                    poll();
              }});
      }
    </script>
    </html>
    
    
    package httplongconnection;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintWriter;
    import javax.servlet.AsyncContext;
    
    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
    
    public class FileListerAdapter extends FileAlterationListenerAdaptor {
    
        AsyncContext ctx;
        public FileListerAdapter(AsyncContext ctx) {
            super();
            this.ctx = ctx;
        }
    
        @Override
        public void onFileChange(File file) {
            if (!file.exists() || !file.canRead()) {
                System.out.println("The file " + file + "  is not exists or is not readable!");
                return;
            }
            try {
                InputStream is = new FileInputStream(file);
                if (ctx.getResponse().isCommitted()) {
                    return;
                }
                PrintWriter out = ctx.getResponse().getWriter();
                String content = org.apache.commons.io.IOUtils.toString(is, "UTF-8");
                System.out.println(content);
                String digest = DigestUtils.md5Hex(is);
                out.write(digest + "02" + content);
                System.out.println("yy");
                System.out.println(content);
                is.close();
                out.flush();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            ctx.complete();
            //TODO 读取操作
            super.onFileChange(file);
        }
    
        @Override
        public void onFileCreate(File file) {
            //TODO 读取操作
            super.onFileCreate(file);
        }
    
        @Override
        public void onFileDelete(File file) {
            super.onFileDelete(file);
        }
    
        @Override
        public void onDirectoryChange(File directory) {
            System.out.println("----The directory " + directory + " has changed.");
            super.onDirectoryChange(directory);
        }
    
        @Override
        public void onDirectoryCreate(File directory) {
            super.onDirectoryCreate(directory);
        }
    
        @Override
        public void onDirectoryDelete(File directory) {
            super.onDirectoryDelete(directory);
        }
    }
    
  • 相关阅读:
    卷积神经网络(Convolutional Neural Network,CNN)
    理解滑动平均(exponential moving average)
    python2到python3代码转化:2to3
    Mac查看和杀死后台进程
    pip安装python库时使用国内镜像资源加速下载过程
    关于pip安装时提示"pkg_resources.DistributionNotFound"错误
    【Linux基础】压缩和解压
    【Linux基础】常用Linux命令: cd, cp, ls, mkdir, mv, rm, su, uname
    【python3基础】相对路径,‘/’,‘./’,‘../’
    剑指offer-学习笔记
  • 原文地址:https://www.cnblogs.com/Spground/p/9567863.html
Copyright © 2011-2022 走看看