必威体育Betway必威体育官网
当前位置:首页 > IT技术

servlet以及spring mvc实现bigpipe技术分享

时间:2019-11-01 04:12:21来源:IT技术作者:seo实验室小编阅读:73次「手机版」
 

bigpipe

使用Servlet分段输出构建BigPipe服务端

BigPipe是一个重新设计的基础动态网页服务体系。大体思路是,分解网页成叫做Pagelets的小块,然后通过Web服务器浏览器建立管道并管理他们在不同阶段的运行。这是类似于大多数现代微处理器的流水线执行过程:多重指令管线通过不同的处理器执行单元,以达到性能的最佳。

使用java实现BigPipe服务端的例子在网上很难寻觅,笔者经过多次尝试,在Servlet3.0和Servlet2.5规范下成功实现了BigPipe的分段输出效果。好东西不敢独享,在这里与大家分享。

搭建BigPipe服务端程序,首先我们必须了解BigPipe服务端的工作原理。BigPipe的最终目标是通过一次请求向浏览器输出页面框架和若干个Pagelet。浏览器在接收完页面框架后立即进行展现,然后并行的接收后面的Pagelets,从而加快页面的渲染速度。Pagelet是通过调用前端的一个JS方法向页面输出的。

为了达到一次请求多次输出的效果,服务端程序必须采用分段输出的技术(chunked)。这里所说的chunked是指HTTP 1.1规范中定义的一个特殊的HTTP头信息,下面的一段HTTP响应头包含了chunked定义:

HTTP/1.1 200 OK

content-Type: text/html;charset=UTF-8

Transfer-Encoding: chunked

通过在HTTP响应头中增加“Transfer-Encoding: chunked”标识,我们通知浏览器后面的响应内容是分段输出的,每段输出使用一个16进制的数字来声明段长度,然后紧跟一个回行换行符,后面是分段的内容体,最后使用一个0长度的分段来结束整个的chunked输出。下面是一个用socket模拟的chunked分段输出:

public static void main(String[] args) throws IOException, InterruptedException {
	StringBuffer content1 = new StringBuffer("<html><body>");
	content1.APPend("<p id='a1' style='width:100px;height:100px;border:1px solid black'></p>");
	content1.append("<p id='a2' style='width:100px;height:100px;border:1px solid black'></p>");
	content1.append("<p id='a3' style='width:100px;height:100px;border:1px solid black'></p>");
	content1.append("<p id='a4' style='width:100px;height:100px;border:1px solid black'></p>");
	content1.append("</body></html>");
	String content2 = "<script type='text/JavaScript'>document.getelementbyid('a1').innerHTML='<h1>1</h1>'</script>";
	String content3 = "<script type='text/javascript'>document.getElementById('a2').innerHTML='<h1>2</h1>'</script>";
	String content4 = "<script type='text/javascript'>document.getElementById('a3').innerHTML='<h1>3</h1>'</script>";
	String content5 = "<script type='text/javascript'>document.getElementById('a4').innerHTML='<h1>4</h1>'</script>";

	ServerSocket ss = new ServerSocket(80);
	Socket s;
	while (true) {
		s = ss.accept();
		s.getInputStream().read();
		printwriter pw = new PrintWriter(new outputstreamwriter(s.getoutputstream(), "UTF-8"));
		//输出响应头
		pw.println("HTTP/1.1 200 OK");
		pw.println("Content-Type: text/html;charset=UTF-8");
		//声明分段输出
		pw.println("Transfer-Encoding: chunked");
		pw.println();
		//第一段
		//先输出段长度,注意必须是16进制,加2是因为后面有个回车换行符
		pw.println(integer.toHexString(content1.length() + 2));
		//输出分段内容
		pw.println(content1.toString());
		//输出回车换行符结束本段输出
		pw.println();
		//立即清空输出缓冲区,通知浏览器立即处理本段内容
		pw.flush();
		//暂停1秒模拟后台处理过程
		Thread.sleep(1000);
		//第二段
		pw.println(Integer.toHexString(content2.length() + 2));
		pw.println(content2);
		pw.println();
		pw.flush();
		Thread.sleep(1000);
		//第三段
		pw.println(Integer.toHexString(content3.length() + 2));
		pw.println(content3);
		pw.println();
		pw.flush();
		Thread.sleep(1000);
		//第四段
		pw.println(Integer.toHexString(content4.length() + 2));
		pw.println(content4);
		pw.println();
		pw.flush();
		Thread.sleep(1000);
		//第五段
		pw.println(Integer.toHexString(content5.length() + 2));
		pw.println(content5);
		pw.println();
		pw.flush();
		//输出一个0长段结束整个输出
		pw.println(0);
		pw.println();
		pw.close();
		//最后别忘了关闭输出流
		s.close();
	}
}
运行上面的代码,在浏览器中输入"http://localhost"应该就可以看到分段输出的效果了,注意和传统的AJAX异步请求不同,这里只有一次请求!

通过上面使用Socket模拟的分段输出响应,我想大家已经了解了Bigpipe服务端工作的原理,下面我们通过Servlet来实现分段输出的效果。查找了好久,网上只能找到使用Servlet3.0实现分段输出的样例,得益于Servlet3.0规范新增的异步处理接口,我们可以使用很幽雅的方式来完成分段输出的效果,但我相信使用旧的Servlet规范一定也能实现分段输出的效果,事实上的确如此,我们先来看使用Servlet3.0实现的代码:

@WebServlet(value = { "/syncServlet" }, asyncSupported = true, loadOnStartup = 1)
public class SyncTestServlet extends HttpServlet {
	private static final long serialversionuid = -126107068129496624L;
	private final blockingqueue<Runnable> quere = new ArrayBlockingQueue<Runnable>(10);
	private final ThreadPoolExecutor.executor = new ThreadPoolExecutor(10, 10, 10, TimeUnit.HOURS, quere);

	public void doGet(HttpServletrequest request, HttpServletresponse response) throws ServletException, IOException {
		response.setcontenttype("text/html");
		final Asynccontext sc = request.startAsync(request, response);
		final StringBuffer content = new StringBuffer("<html><body>");
		content.append("<p id='a1' style='width:100px;height:100px;border:1px solid black'></p>");
		content.append("<p id='a2' style='width:100px;height:100px;border:1px solid black'></p>");
		content.append("<p id='a3' style='width:100px;height:100px;border:1px solid black'></p>");
		content.append("<p id='a4' style='width:100px;height:100px;border:1px solid black'></p>");
		content.append("</body></html>");
		final PrintWriter pw = response.getWriter();
		pw.println(content.toString());
		pw.flush();
		executor.execute(new Job(sc, 1));
		executor.execute(new Job(sc, 2));
		executor.execute(new Job(sc, 3));
	}

}

class Job implements Runnable {
	private static int count;
	private AsyncContext syncContext;
	private int no;

	public Job(AsyncContext syncContext, int no) {
		this.syncContext = syncContext;
		this.no = no;
		count++;
	}

	public void run() {
		HttpServletResponse resp = (HttpServletResponse) syncContext.getResponse();
		try {
			Thread.sleep(no * 1000);
			PrintWriter out = resp.getWriter();
			out.println("<script type='text/javascript'>document.all.a" + no + ".innerHTML='<h1>" + no + "</h1>'</script>");
			out.flush();
		} catch (IOException e) {
			e.printstacktrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		count--;
		if (count == 0) {
			syncContext.complete();
		}
	}
}

下面是使用旧的Servlet规范实现分段输出的代码,由于旧的Servet规范没有包含异步处理功能,我们只能自己处理线程的同步和页面输出流的抢占问题。

public class BigpipeServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
		final PrintWriter pw = response.getWriter();
		final StringBuffer content = new StringBuffer("<html><body>");
		content.append("<p id='a1' style='width:100px;height:100px;border:1px solid black'></p>");
		content.append("<p id='a2' style='width:100px;height:100px;border:1px solid black'></p>");
		content.append("<p id='a3' style='width:100px;height:100px;border:1px solid black'></p>");
		content.append("<p id='a4' style='width:100px;height:100px;border:1px solid black'></p>");
		content.append("</body></html>");
		pw.println(content.toString());
		pw.flush();
		final Paglet paglet1 = new Paglet(pw, 1);
		final Paglet paglet2 = new Paglet(pw, 2);
		final Paglet paglet3 = new Paglet(pw, 3);
		paglet1.start();
		paglet2.start();
		paglet3.start();
		try {
			paglet3.join();
			paglet2.join();
			paglet1.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		pw.close();
	}

	class Paglet extends Thread {

		private int id;
		private PrintWriter out;

		public Paglet(PrintWriter out, int id) {
			this.out = out;
			this.id = id;
		}

		@Override
		public void run() {
			try {
				sleep(id * 1000);
				final String content = "<script>document.all.a" + id + ".innerHTML='<h1>" + id + "</h1>'</script>";
				synchronized (out) {
					out.println(content);
					out.println();
					out.flush();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

使用Spring mvc来实现和bigpipe技术,可以采用servlet的原理,在所使用的controller方法获取到response对象,返回值设置成void,采用flush进行分块输出。

文章最后发布于: 2015-12-13 09:46:18

相关阅读

MVC三层框架详细解析

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识,造福人民,

HttpServletRequest参数获取,HttpServletRequest详解

--------------------------HttpServletRequest参数获取,HttpServletRequest详解--------------------------------- HttpServletR

Spring框架中@DateTimeFormat和@NumberFormat的用法

@DateTimeFormat是用来验证输入的日期格式;@NumberFormat是用来验证输入的数字格式。有时候,因为输入习惯或某些要求必须改变格式的

spring cloud框架介绍

直接引用经典文章来源:https://blog.csdn.net/w05980598/article/details/79007194什么是微服务微服务的概念源于2014年3月Martin

springMVC 入门配置(方便我以后快速复习)

1.0 springMVC 的作用 springMVC 在我看来是用spring的有点来实现MVC架构 这样可以整合许多优秀的框架中的功能例如本篇重点介绍

分享到:

栏目导航

推荐阅读

热门阅读