当前位置: 首页 > news >正文

东莞网站公司推广技巧wordpress七牛远程图片

东莞网站公司推广技巧,wordpress七牛远程图片,网站 对比,淘宝网店怎么注册开店JavaEE平台技术——预备知识#xff08;Web、Sevlet、Tomcat#xff09; 1. Web基础知识2. Servlet3. Tomcat并发原理 1. Web基础知识 #x1f192;#x1f192;上个CSDN我们讲的是JavaEE的这个渊源#xff0c;实际上讲了两个小时的历史课#xff0c;给大家梳理了一下Web、Sevlet、Tomcat 1. Web基础知识2. Servlet3. Tomcat并发原理 1. Web基础知识 上个CSDN我们讲的是JavaEE的这个渊源实际上讲了两个小时的历史课给大家梳理了一下这二三十年来JavaEE的规范和Spring的规范如何是相生相克的。 我们在整个课程设计中间或这门课中间会用到一些什么样的相关知识有一部分的知识可能很简单大家就当听听过去了有一部分知识可能大家没有接触到可能听不懂也没关系知道我们会用到它然后知道它用的目的是为了什么之后的话大家在课后或者用到它的时候再去深入的去了解这些相关的预备知识。 引出HTTP 我们首先从最简单最基础的开始讲起啊我们知道JavaEE的规范主要是用来做后台服务器系统的后台的服务器系统基本上都是现在是HTTP协议的一统天下基本上我们不会再用其它的协议去实现我们的后台的服务器系统。 引出统一资源定位符URL 在HTTP的这样的一个协议上头它就是它依赖的是我们的统一资源定位符Uniform Resource Locator大家都很熟悉了它其实是由这么几个部分构成。 资源定位符URL的构成 协议我们主要用的是HTTP的协议所以这个部分是HTTP或者HTTPS服务器的域名那域名实际上对应的是IP我们会用域名服务域名去查那个IP所以IP是什么其实是没有关系的这里看到的是一个DNS的域名我们在用的时候其实我们往往不会用DNS的域名而是用我们自己的一个名字DNS域名是到DNS服务器上去查我们自己的名字其实就是到我们的服务器上去查那个名字所对应的IP所以后面会看到我们的程序其实可以任意装在任何一个地方只要我们给它提供了查名服务用名字去查到这个IP这个问题就能够解决这是第二个部分。 DNSDomain Name System域名系统是互联网的一项基础设施技术。它的主要功能是将易于记忆的域名例如www.example.com转换为IP地址例如192.0.2.1以便在网络上定位和识别计算机服务和设备。DNS域名系统可以看作是互联网的电话簿它通过将域名映射到相应的IP地址来帮助用户定位所需的网络资源。 在实际应用中当您在浏览器中键入网址时浏览器首先会向DNS服务器发送查询请求以获取相应域名所对应的IP地址然后才能建立连接并加载所需的网页内容。这个过程使得用户无需记住复杂的数字地址而是能够使用更易于记忆的域名来访问网站。 端口号因为在同一台服务器上可以装多个应用所以每个应用需要自己的一个端口号你才会知道说你访问这个服务器的时候是访问的是这个应用而不是其它的应用这是端口号。后面的东西是路径路径通常是用来在一个应用里头去标识说这个部分是什么样的东西这个路径其实它并不代表说在这台服务器上的某个文件的目录它就是用来去标识去区分在我们的应用之间去区分说这是应用的这半部分还是这是应用的另外一个部分比如说我们以后在用到路径的时候通常是用来区分什么区分模块我们会有若干个模块那就会用路径去区分这个模块这是路径。最后则是资源就是我们真正提供的东西这个不是一个文件往往可能是我们在服务器上头的一段代码。 现在我们拿到应该网址我们可以清楚的看到以下部分是什么 我们会描述了我们用什么样的协议访问哪台服务器的哪个端口在这台服务器的端口的应用里头我们要访问哪一部分以及这一部分的什么东西构成了这样的一个URL。 应该大家都熟悉在web的服务中间访问的过程这样子我们前端不一定是人也可能是另外一台服务器它通过一个URL的地址发送到一台服务器上它中间会有比较复杂的查名过程所以它查到这个服务器的IP然后把这个请求发到服务器上然后服务器就根据端口根据路径然后根据资源就是后面的三个部分去找到对应的东西去执行它得到一个结果这个结果再返回给前端然后前端去用这个结果我们在图上看到是HTML我们现在在这个过程里面很多的时候是一段数据就是发一个请求向服务器去执行一个什么样的东西然后服务器通过这个端口知道是什么样的应用通过后面的路径知道是应用的哪一块部分通过资源知道说我在这块部分要执行什么然后执行它得到一个结果把这个结果再返回给前端这就是这样的一个访问过程我想这个应该大家都很熟悉。 其中HTTP协议或者HTTPS协议是这个里头最基本的部分最上面一层应用层的协议对吧所以它这种协议是要基于底层的TCP/IP来实现的所以它是一层一层的啊网络的7层协议。 我们这里讨论的就是它这个http协议的特点在于说——它是一对的一问一答发出一个请求我们称之为HTTP的Request然后它返回一个HTTP的Response不问不答如果我不发HTTP Request它不会回来一个HTTP Response而且我不会记录之前问过什么所以它的每一个相关的内容都应该是基于它的HTTP的Request去返回HTTP的Response。 HTTP之后我们会引出Request Response 当然我们在应用中间我们会发现HTTP的协议的这个特性其实挺挺恼火的因为我们确实是需要知道它之前干了什么但是HTTP协议本身来说它只提供一问一答的这样一个机制所以我们要靠其它的部分来完成这个东西。 HTTPS是在这个协议上做了加密 HTTP协议是明文的所以我们如果说想要对我们在网络上头传输的内容无论是Request的内容还是Response内容进行加密的话进行保密处理的话都应该使用HTTPS的协议。 原来Google警告这么来的 所以大家现在可以看到基本上所有的应用都是用的HTTPS不是HTTP甚至极端情况下的比如像谷歌的浏览器你用HTTP它要警告你说这个服务器用的是HTTP是不安全的你确定要不要用你用才能继续访问它。 虽然我们在课上一直是用HTTP的协议来举例子但真实在用的时候其实是用HTTPS但是无论是HTTP还是HTTPS对于我们的程序来说其实没差别的它是一个网络传输层的问题它是用了加密还是没有用加密的传输对于我们程序来说我们其实是不关心它是用HTTP还是HTTPS的。 只是在配置上如果说要用HTTPS的话在Web服务器的配置上是要做些很特别的配置不管是HTTP还是HTTPS的协议它都是HTTP的Request和Response的过程所有的协议都是分为包头和正文的部分就是Head和Body的部分Head是控制信息。 我们为什么要把这个协议的控制信息拿出来讲——因为我们有很多重要的数据都在控制信息里头 控制信息其实我们在写程序的时候我们也会人为的在Head里头 在头里头插入我们所需要的控制信息我们在服务和服务之间调用的时候我们也会在转发 HTTP协议时候加入我们所需要的头的信息。 所以每年在做课程设计的时候同学们在做微服务体系结构的时候会犯错的一个主要的地方就是这个协议的头没控制好调进来的时候是ok的然后再去调别人就不ok了出现这种情况你首先就应该怀疑说这个头有问题你要看看它进来的头ok的头是什么你再去调别的时候你应该保持一样的头才能保证整个的调用是连贯的所以头的格式大家应该都很清楚它描述了请求的方法。 HTTP Request 部分 Head的大致论述 我们后面讲HTTP有若干种请求的方法也是跟我们的设计有关因为我们后面会讲RestFul的API其实它就利用 HTTP的请求的方法去做 API的设计然后是URL然后是它的协议的版本号然后是它的主机然后后面它的控制字符我们在里头会要用到其中的一些控制字符或者要去加一些控制字符就是它的变量它的头里头所放的这些字段名和值这是我们要用到的东西。 Body的大致论述 Body里头放的就是内容在Body里头通常放的就是一个JSON的字串一个JSON的内容放在里面对于这个请求的方法就是HTTP的Request方法这个也应该知道我们一共有OPTIONS GETPOST PUT DELETECONNECT和TRACE这些方法。 请求方法描述OPTIONS返回服务器针对特定资源所支持的HTTP请求方法GET向特定的资源发出请求。请求体中无内容POST向指定资源提交数据进行处理请求。数据被包含在请求体中。PUT从客户端向服务器传送的数据取代指定的文档的内容。DELETE请求服务器删除指定的页面。CONNECTHTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。TRACE回显服务器收到的请求主要用于测试或诊断。 我们其中会用到的部分来说主要是前面的5个就是OPTIONS GET POST PUT DELETE这五种方法这5种方法在RestFul API中间它是有特别的定义的就是我们会利用HTTP的请求针对同一个URL的不同的请求的方式去做不同的语义然后去设计说这是用来去实现什么样特定的功能。 HTTP Response部分 Response部分跟Request的部分一样仍然分为包头和正文。 包头依然是控制信息所以我们在Response的包头部分也会要插入一些我们所要的东西。 而Body的部分主要是JSON的数据我们会以JSON的格式把数据放到正文里头放到Body的部分这是HTTP的协议。 最后是它的状态码状态码是在HTTP的Response里头的字段大家应该知道在头里头它应该会返回它的状态码是什么。 这个状态码表示了什么 表示了服务器端向客户端返回说服务器端的你发的请求它的结果是什么。 成功 我们最常见到的结果是200和201200和210都是ok说明服务器一切正常你无论是一个请求还是一个POST的请求 GET请求都是200POST的请求的返回的ok是201这是它的规范所决定的。 400系列小解读 其它的这些值就代表特别的含义比如说我们说400开头的这些东西通常是你发过来的东西有问题比如说你发过来的 Request它的正文的值我解释不出来不符合我的格式所以就会以400的方式 你发过来的请求要访问的这个程序是需要登录的但是你没有登录就会返回401你如果登录了你来访问它这个部分如果你没有权限它就会返回403如果你要请求的 URL根本就是错的在我这里头就不知道这是个什么东西我就会给你返回404 所以400系列的状态基本上是跟你的请求有关的就是你请求有问题说到底你请求有问题就会返回400类的错误码这些错误都是会用程序去完成的比如说我们在解析它的一个Request的时候发现它对不到我们的一个参数上那就会返回400。 500系列小解读 500开头的错误通常就是我们服务器本身有问题。 比如说你的服务器崩掉了你就会返回500。比如说你的服务器阻塞了就是我服务器没崩但是我服务器已经响应不过来了就会返回503。 所以400的系列的错误是表示说Request本身有问题500的错误是表示说Request没问题但是我服务器有问题Response返回里头值是没有意义的就会返回500的错误这是我们主要的状态码 对于所有的状态码都是有定义的就是我们除了这里所看到的400和500的状态码以外我们在这个基础上头针对比如说200 201的状态码上头我们还有新的错误码去定义表示说更新的含义但是状态码我们还是符合HTTP协议所规定的状态码在它这个语义上头去扩充我们需要返回的更多的值这是这个状态码。 2. Servlet 简单来个小导读 这个部分我们要讲的就是关于Servlet它是一个有着悠久历史JavaEE的第一版的规范Servlet就出现了然后到JavaEE的最后一版规范它还在它 为什么会这么长的时间存在于JavaEE的规范中间Spring中也有这个规范 因为它是我们目前一个非常重要的技术规范也是重要基础的规范。在Spring的框架中间的Servlet Stack的所有的技术都是基于Servlet规范来实现的。 我们略微讲一下Servlet规范的定义是什么以及它的实现机制是什么 虽然我们今天已经很少有机会去直接来写这部分的Servlet的代码但是因为它是我们整个Servlet Stack的一个最基础的东西所以我们还是有必要去了解它的实现的机理这样的话会方便我们去理解后面的东西是怎样在这个机制上头去实现的以及我们如何去调优我们的服务器特别是Servlet Stack的这个调优其实跟Servlet的机制是有密切的关系的。 Servlet这样的一个机制我们在JavaEE的规范中间其实有提到它是用来去做服务器的表现层的最开始是出于这样的一个目的它接收的是HTTP的Request。 根据这个HTTP的Request它把服务器端分成了容器和组件两个部分。容器叫做Servlet容器用规范去定义了这个容器该做什么。组件叫做Servlet规范去定义了Servlet要做什么。 当一个HTTP的Request过来的时候它是发到容器容器则会根据这个HTTP的Request去运行Servlet执行一些逻辑这个逻辑就是用Java代码写的你爱做什么做什么比如说我们这里看到的是访问数据库然后得到数据产生你的HTTP Response里头要放的那些数据给它送回来这样的一个机制。 我们要稍微深入讨论的是内部发生了什么 这就是JavaEE的Servlet的规范里头所定义的东西 JavaEE的Servlet的规范里头定义了一系列的接口和一系列的类我们这个里头这张图上头只有第一个接口是有写了包名的 其它的没写不是说它没有包名它的包名跟第一个都是一样的全在javax.servlet的包里头这个就是JavaEE的Servlet的规范定义了一系列的接口和基类。 这一个接口里头定义了什么 你要去实现一个Servlet你首先要去实现一个叫做javax.servlet.Servlet的接口这个接口里头有很多的方法其实它最重要的一个方法就是service方法service方法有两个参数一个是ServletRequest一个是ServletResponse就这张图上的这个东西我要实现一个这样的对象这个对象必须实现Servlet的接口这个接口里头有service的方法这个方法接收过来的是HTTP的Request返回的是HTTP的response送过来的Request返回去的response作为接口的两个参数。 但它在这个接口上没有写HTTP两个字原因是它在设定规范的时候它想Servlet其实还可以在其它的网络协议上去实现但事实30年过去了Servlet也没有在其它的网络协议上去实现但是它是想这么来做的。 这左右两个接口又有子接口这个子接口才是HTTP协议的HttpServletRequest和HttpServletResponse这就具体到我们真正的HTTP协议上了。 对于Servlet的接口来说它有实现了基类但是基类做了两层。 一个是GenericServlet一个是HttpServlet大家知道这个HttpServlet就是用来去实现HTTP协议的Servlet在这个基类中间大家可以看到它实现了一系列的do的方法这些do的方法全是两个参数分别是HttpServletRequest和HttpServletResponse全是这两个参数这两个参数是这个ServletRequest和ServletResponse的子类。 这张图就画的是我刚才所说的这样的一个事情对于Servlet容器来说它接到HTTP的Request以后它把Request变成Request对象然后创建了一个response的对象。 然后把Request对象和Response对象Servlet是一个对象实例化成一个对象通过一个线程去调Servlet的service的方法 service的方法会根据请求的类型去调doGet还是doPostdoPut还是doDelete然后把传过来的 HTTP的Request对象和HTTP的response对象传给它里头你自己去写代码就这样的一个过程。 JavaEE规范的特征——就是它针对我们在JavaEE规范中间所定义的接口和基类。 你去继承或者实现这些接口和基类在它的规范下去写代码你就是符合它的规范的 符合它的规范有什么样的好处Servlet它的容器是可以替换的我们目前常见的Servlet的容器有两种Tomcat、Jetty。 Tomcat是大家最熟悉的我们一般在写程序的时候可能优选就用Tomcat用Tomcat以后你会发现说有时候它特别慢然后你就想说我能不能换一个容器它的速度会更快一点Jetty就是一个比Tomcat更加高效的容器但是它用的比较少。 我们有一段演示的代码如何在JavaEE规范上去写一个Servlet当然这个代码的主要的作用只是说给大家演示一下代码怎么写然后大家去理解Servlet的这样一个机制我们今天基本上是不会去手写Servlet代码了但是我觉得还是有必要大家看一下那个代码怎么写的在这里我们之所以给大家来讲这个Servlet的代码目的并不让大家去学习如何基于JavaEE的Servlet的规范来写代码因为这个部分的技术其实已经过时了今天我们是不会再来写Servlet代码的主要的目的就是让大家去了解Servlet的原理因为它到今天依然是我们Servlet Stack的一个最基础的部分这个代码非常的简单。 我们一共写了三个Servlet第一个Servlet是WelcomeServlet package cn.edu.xmu.javaee.servlet;import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;public class WelcomeGetServlet extends HttpServlet {Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType(text/html;charsetUTF-8);String firstName req.getParameter(firstname);firstName new String(firstName.getBytes(ISO-8859-1),UTF-8);PrintWriter out resp.getWriter();out.println(?xml version\1.0\ encoding\UTF-8\?);out.println(!DOCTYPE html PUBLIC \-//W3C//DTD XHTML 1.0 Transitional//EN\ \http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\);out.println(html xmlns\http://www.w3.org/1999/xhtml\);out.println(head);out.println(title处理get提交的数据/title);out.println(/head);out.println(body);out.println(h1欢迎使用Servlet/h1br/);out.println(提交的姓名是firstName);out.println(/body);out.println(/html);out.close();} }我们可以看到就像我们前面在课上所说的一个Servlet的代码是需要实现一个特定的基类的所以我们可以看到我们的WelcomeServlet类extends了HTTPServlet就是我们前面所说的实现了HTTP协议的Servlet基类。 在WelcomeServlet的代码中间其实非常的简单里头只写了一个方法就是我们重写了它的doGet的方法这意味着说我们会去响应它的get的请求。doGet的方法里头两个参数大家可以看到HttpServletRequestHttpServletResponse。 既没有从HttpServletRequest拿到任何的数据 从HttpServletResponse中间获得它的Writer这个Writer的目的就是向Http Response中间写入数据当然这我们写的数据是一串的HTML这个HTML就是为了打印出来一个欢迎使用Servlet这样的一个欢迎语所以把这一堆的HTML的东西通过out把它输出到Http Response里头去那它就会被送到前端浏览器就会解析这一堆送过去的HTML然后就把它呈现出来 第二个例子我们就稍微复杂一点我们写了一个HTML把一些数据传过去Servlet的工程中HTML其实是放在另外一个目录里头的我们把它叫做webapp目录在这个目录底下我们放了一个get.html的文件 !DOCTYPE html html langcn headmeta charsetUTF-8titleHTTP Get 请求/title /head bodyform action get methodgetplabel输入姓名点击提交br/ input typetext namefirstname/input typesubmit value提交//label/p/form /body /html当然还有我们后面要讲的post.html文件所有的HTML文件包括图片等等静态文件都是放在webapp的目录底下我们看一下get.html其实非常的简单它里头就是一个form action在 form action中间我们主要是提供了一个input框这个input框是要向前端去提交一个你的姓名的 package cn.edu.xmu.javaee.servlet;import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;public class WelcomeGetServlet extends HttpServlet {Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType(text/html;charsetUTF-8);String firstName req.getParameter(firstname);firstName new String(firstName.getBytes(ISO-8859-1),UTF-8);PrintWriter out resp.getWriter();out.println(?xml version\1.0\ encoding\UTF-8\?);out.println(!DOCTYPE html PUBLIC \-//W3C//DTD XHTML 1.0 Transitional//EN\ \http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\);out.println(html xmlns\http://www.w3.org/1999/xhtml\);out.println(head);out.println(title处理get提交的数据/title);out.println(/head);out.println(body);out.println(h1欢迎使用Servlet/h1br/);out.println(提交的姓名是firstName);out.println(/body);out.println(/html);out.close();} }回到Servlet代码我们回来看WelcomeGetServlet同样继承的是HttpServlet的它也是个get请求所以同样重载了这个doGet的方法它从Http Resquest中间用它的getParameter的方法去获得了我们从前端传过来的值。 然后把这个值输出到了Http Response里去当然作为HTML输出到Http Response里去这就是一个非常简单的get但是跟我们前面那个例子相比它从Http Resquest中间去拿到了一些值。 package cn.edu.xmu.javaee.servlet;import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter;public class WelcomePostServlet extends HttpServlet {Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType(text/html;charsetUTF-8);req.setCharacterEncoding(utf-8);resp.setCharacterEncoding(utf-8);String firstName req.getParameter(firstname);String bornPlace req.getParameter(bornplace);PrintWriter out resp.getWriter();out.println(?xml version\1.0\ encoding\UTF-8\?);out.println(!DOCTYPE html PUBLIC \-//W3C//DTD XHTML 1.0 Transitional//EN\ \http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\);out.println(html xmlns\http://www.w3.org/1999/xhtml\);out.println(head);out.println(title处理get提交的数据/title);out.println(/head);out.println(body);out.println(h1欢迎使用Servlet/h1br/);out.println(提交的姓名是firstName);out.println(br/提交的籍贯是bornPlace);out.println(/body);out.println(/html);out.close();} }最后我们看一下HTTP的post的这种请求是怎样实现的post通常是用来传更多的值当然这个例子中间我们其实就传了一个非常少的两个值依然是依赖于 HTMLpost.html来把两个值把它送过来对于Servlet的这一端来说它依然还是一个HTTP的Servlet但是我们重载了doPost的方法依然是两个参数HTTP的RequestHTTP的response无论是 get的请求还是post的请求其实它的值都放到了HttpServletRequest里头所以我们依然从HTTP的Request里头去获得我们从前端传过来的两个值然后又把它写到了Http Response去然后把它送回前端。 !DOCTYPE html html langen head meta charsetUTF-8 titleHTTP Get 请求/title /head body form action post methodpostplabel姓名: input typetext namefirstname/br/籍贯input typetext namebornplace/input typesubmit value提交//label/p /form /body /html我们看一下这些代码和HTML是怎样能够合起来被Servlet的容器知道说我有多少Servlet的它依赖的是一个叫做web.xml的配置文件在webapp/WEB-INF的目录下当然这个是约定在这个目录下它才会去认web.xml文件在 web.xml中间其实主要是两个部分。 ?xml version1.0 encodingUTF-8? web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsdversion4.0display-nameArchetype Created Web Application/display-nameservletservlet-nameWelcome/servlet-name!--包名类型--servlet-classcn.edu.xmu.javaee.servlet.WelcomeServlet/servlet-class/servletservletservlet-nameWelcomeGet/servlet-name!--包名类型--servlet-classcn.edu.xmu.javaee.servlet.WelcomeGetServlet/servlet-class/servletservletservlet-nameWelcomePost/servlet-name!--包名类型--servlet-classcn.edu.xmu.javaee.servlet.WelcomePostServlet/servlet-class/servletservlet-mappingservlet-nameWelcome/servlet-nameurl-pattern/Welcome/url-pattern/servlet-mappingservlet-mappingservlet-nameWelcomeGet/servlet-nameurl-pattern/get/url-pattern/servlet-mappingservlet-mappingservlet-nameWelcomePost/servlet-nameurl-pattern/post/url-pattern/servlet-mapping /web-app一个部分是Servlet的mapping就是我们看到的下面这个部分这个部分主要是把我们的请求映射到对应的Servlet上头所以我们可以看到映射到了名字叫做Welcome的Servlet的上头。 一个部分都是关于Servlet的定义我们可以看到它定义了三个Servlet对应的就是我们说的Servlet层的名字还有我们的代码中间的Servlet的类所以这样的话Servlet的容器才会根据请求去从线程池里头去拿到线程去执行对应的Servlet的类。 3. Tomcat并发原理 这样跑的一个过程我们再要讨论一下它的细节它是用多线程来跑的我们做的是服务器端的一个应用服务器端一个应用同时都要响应很多的请求就是有不同的客户端会同时发过来很多的HTTP的Request针对每一个HTTP的Request在Servlet的容器中间都是以一个独立的线程去跑。 就我们这张图上其实有看到它是用一个独立的线程去跑的会起一个线程里头去调Servlet对象的service的方法Servlet的对象是唯一的因为我们在代码中间会写很多的Servlet的对象然后这些Servlet的对象会被很多的请求来调。 如果说Servlet的对象不唯一的话内存吃得太厉害所有的对象就只有一个。 另外一个问题Servlet的对象是不能有属性的线程和进程的差异线程是共享Java虚拟机的内存空间的a线程和b线程如果说它去用一个Servlet的对象它们俩是同一个对象它们俩的属性就是同一个属性如果a线程改了b线程就会受影响就会出现这样的问题为了避免这样的问题简单的方法就是Servlet对象是没有属性的没有属性你就不存在改的问题它在不同的线程里头去调的话也不会打架。 在比如说service方法或者doGet方法中间的局域变量局域变量是不会有问题的在Java虚拟机里头不同的线程是有线程栈的不同的线程它的局域变量全是分开的分别保存在自己的线程栈里头这里就会带来另外一个调优的问题如果当线程很多的时候线程栈占的空间也会很大所以你要适当调整线程栈的大小才能容纳更多的线程不是说你简单的把线程调到1万它就能够跑的调到1万线程栈不够了它也跑不了。 所以 线程与线程栈 是有对映关系的。 为了避免这个问题我们对象通通不写属性凡是会有存在的线程冲突的这些对象我们都不写属性这样的话它线程就是安全了就没有问题。 每一个请求用一个线程去执行这个线程里头会去调Servlet的service方法其实在这个容器中间在执行线程的时候它是分成了5步哪5步 Read的重要性——读为什么还要单独写一个 因为网络的特性它并不是一个可靠的稳定的你永远是一个匀速的在读一个东西你的网络会受到各种各样的因素影响比如说你从美国发一个请求过来Request读起来可能从头读到到完整的读完大家知道HTTP是应用层协议底下的TCP/IP协议可能一个Request会被分成了若干个TCP/IP的包所以说你拿到第一个包到最后一个包完整读完最后拼成一个HTTP的Request可能是要不知道多少时间完全是看对面发过来的有多远网络的状况是怎么样会不会丢包会不会重发都不知道read的过程其实是完整的读完HTTP的Request的过程这个是要时间的。 Decode的重要性 读完以后读到的是一个Head和Body的这样的一个协议然后它要转成一个HTTP的Request的对象实现了一个接口的对象所以它要做decode就是把网络协议读到的数据变成我们的一个Java的对象这叫做decode。 Compute的重要性 decode的主要是指 Request的对象拿到这个对象以后创建一个Response对象一起传给了service的方法。 Service方法中间会去根据你的请求的类型去调doGet还是doPost还是doDelete等等这就是我们说的compute过程因为那个方法是你自己写的就数据已经拿到了你去做逻辑然后产生结果这个都是compute的过程。 Encode的重要性 拿到这个结果以后你把它丢到 Response里头返回回去它要把它变成Response的协议那是encode的过程。 send的重要性 变成协议以后再传回给客户端那是send的过程同样传回去的数据. 在TCP/IP上有可能如果比较大的话可能会分成若干个包然后一个包中间会有丢包所以send的过程也是不可控的整个这个过程都是在一个线程中间去完成的这样的话它用多线程的方式就是多少个请求开一个线程我们Servlet对象的不写属性大家相互不打架所有的局域变量都在自己的线程栈里头所有的读写decode encode都各自在线程中间去处理这样就能做到并发。 这样的话会有一定的问题我们其实对于一台具体的物理机上是没有办法无限制的增加线程的为什么没办法无限制增加线程 我们已经说到每个线程要占一定的内存空间针对一台特定的物理机内存是有限的你不可能无限制的1万 2万 3万 4万的线程上去内存就没有了。第二个是你的计算资源是有限的因为你任何一台物理机的CPU就是那么多核或者就是那么多个CPU你中间这个计算过程就要靠CPU来完成。 如果你无限制的增加线程的话我们在操作系统中间会讲到说CPU会忙不过来 CPU就会要不断的分片轮转然后CPU就会把大量的时间花在这个轮转上头其实它并不是一个高效的方式我们会根据一台物理机器的实际状况它有多少内存它的CPU的计算能力再结合我们任务的这样一个情况去制定一个线程池。 线程池是什么 就是我们最多允许你跑多少个线程。如果多于这个线程的话怎么办就让它去等待。如果在多于等待数的时候我就拒绝这就是线程池的概念。 有了这个线程池以后我们实际上是预先创建了若干个线程放到线程池里头因为孵化线程是需要消耗时间的要用代码来消耗时间的比如说内存中间开一些空间等等所以我们把线程事先孵化出来把它放到线程池里头当请求过来的时候我们是从线程池里头拿空闲的线程拿出来直接去用的这样可以加快它的响应时间而且可以管理整个服务器上对于内存和CPU的消耗不会出现说它的请求多了它的内存不够它的服务器崩溃的这样的一种情况因为它用线程池卡住了它的上限。 其实我们现在如果在做是Servlet Stack这一条技术线的时候我们始终会关心这么4个参数 #最大工作线程数、默认200 server.tomcat.max-threads 200 #最大连接数默认是10000 server.tomcat.max-connections 10000 #等待队列长度默认100 server.tomcat.accept-count 100 #最小工作空闲线程数默认10 server.tomcat.min-spare-threads 100max-threads这个就是线程池的上限这个就是因为你的服务器的特性你的物理性能决定的你有多少内存你的CPU计算能力是多少所以你要定一个上限上限不能超过你的物理资源所提供的能力accept-count这个是排队队列的长度对如果说发来的请求超过了你的处理的上限的话我还可以让它排队等待不是直接拒绝它我现在定的是100就意味着说上限是200它最多可以同时处理多少300个请求其中200是在处理的100是在等待的如果超过300以后就会怎么样就会拒绝就是我们刚才看到的503 server unavailable就直接就拒绝了发挥503的错误码回去这是根据你的服务器的能力来决定的max-connections最大连接数通常我们不会动因为它默认值是1万我们现在基本上我们的服务器很难达到1万的这样的一个东西1万是代表什么意思是max-threads加上accept-count这些连接全部都在的就是你正在处理的最大数和你让它等待的最大数加起来就是它最大连接数所以我们现在是300它默认值是1万还远的了所以一般来说我们不会去动它min-spare-threads这个是在说线程池的问题当然我们可以说线程池有200我们一起来就占200占200你觉得有点浪费了有些时候因为占了200以后它其实会占一定的内存空间但其实很多时候我们其实还没有到200所以我们会定一个min-spare-threads就是线程在启动的时候它只有100个线程在里头如果说超过了100个线程它就开始孵化线程放进去上限是200。然后如果说它的压力下来了就是没有200个请求慢慢下来了它又会把孵化的线程给它释放掉释放出我们物理机的一些内存空间下限又会降到慢慢最后降到100少于100请求以后它就一直保持100个线程线程在里头少于100个线程它也不会再去释放线程池里的线程了所以这是一个下限。 这4个参数是我们之后会特别是在用Tomcat时候会频繁去调的参数这个参数其实不是一个恒定的值这个值其实除了跟你的服务器的物理的性能有关以外还跟你的应用有关你的应用到底是io密集型的还是计算密集型的。 如果说你的应用是io密集型的意味着什么因为这在read和send的过程中间是需要消耗尽量多的时间的计算的时间其实是比较少的意味着如果你内存足够大的话你可以尽量多开线程就是max-threads可以开很高开1000 2000都没问题因为对CPU压力不大只要内存够你就尽量的开到顶。 如果说你是计算型的就是你主要会消耗它的CPU的资源意味着说CPU就是你的上限你不能说内存足够你就开很多的线程那个没用因为CPU只算那么快你开到1000 2000它也算不过来。 所以到底会开多少线程数你其实要去分析说你的应用到底是io密集型的还是计算密集型的去综合决定说你的线程数是一个什么样的值这个是多线程管理。 什么是NIO的模式和APR的模式 它们都是异步io的模式差别在于NIO用的是Java来实现的APR用的是原生库大家知道Java的字节码的运行效率是低于原生库的APR利用的是阿帕奇的HTTP的原生库来实现的异步io 这个就意味着说你不能单纯的装一个Tomcat就好了你还需要先装阿帕奇的原生库再装Tomcat才能跑出它的APR模式所以它装起来会稍微麻烦一点NIO的话还是基于Java来做的所以说就不需要去装原生库我们现在的新的版本的默认方式是NIO。 Compute BIO和NIO的差别 所谓BIO即我们前文所述的指的是在线程中进行的I/O操作。因此在其内部代码实现过程中正如我们刚才所观察到的那个过程客户端发出请求在其Servlet容器的代码中首先是一个名为Acceptor的对象。 该Acceptor对象接收到客户端的请求后随即创建一个SocketProcessor。 大家应该对这个名字有所了解它与网络通信有关它打开了一个Socket并将此对象传递给从线程池中获取的一个线程。 正如我们所提到的我们使用线程池来控制线程数量的上限和下限。因此它从线程池中取出一个空闲线程并将SocketProcessor交给该线程然后开始读取网络数据。 整个读取过程都在线程范围内完成。如果读取速度很慢它将一直占用线程。当读取完成后线程才会调用Servlet对象的service方法然后根据请求类型调用其doDelete、doPut或doGet方法。读取完成后它会生成一个HTTP响应并依靠SocketProcessor将HTTP响应传回给前端。 无论是产生HTTP请求还是获取HTTP响应整个过程都是在线程中进行的。直到最后完成并将其放回线程池供下一个请求使用。因此它被称为阻塞式I/O方式也就是所谓的Blocked IO。之所以如此命名是因为在线程的I/O过程中该线程一直被占用。这是传统模式默认情况下仍采用这种模式。 这种模式与传统模式的不同之处在于I/O不在线程范围内。它是如何实现的呢 前面提到请求仍然由一个Acceptor处理当请求到达时也仍然是一个Acceptor。然而在这种情况下它不是创建一个SocketProcessor而是创建一个名为NioChannel的对象。 这是什么呢它同样能够接收来自客户端的数据并将其放入缓冲区中。关键是当传输完成后NioChannel将产生一个事件表示数据已经读取完成。 因此它会将NioChannel放入一个事件列表或队列中。无论前面有多少个请求过来它都会为每一个请求产生一个NioChannel并将其放入列表中。此时与线程无关它只是将其放入列表中。 然后使用一个独立的Poller线程来轮询列表。这个轮询的作用是查看哪个通道已经读取完成。 一旦某个通道读取完成后续操作与我们之前所描述的BIO相同。它仍然会创建一个SocketProcessor将读取完成的数据交给SocketProcessor然后将其交给一个线程。接下来的步骤与之前相同。 首先是读取过程。当线程获得SocketProcessor时将数据从读取状态转换为HTTP请求时此时没有延迟因为数据已经准备好并放入缓冲区所以可以直接进行解码并将其转换为HTTP请求对象。然后执行后续操作如调用service方法根据请求类型调用doGet、doPut或doDelete方法。在获得HTTP响应后它仍然会将其返回给SocketProcessor然后将其传回NioChannel。线程就完成了其使命然后再次回到线程池中。 NioChannel会负责将数据传回给前端。整个I/O过程都发生在这个过程中而不是在线程的范围内。线程中只有我们前面提到的部分或者是这个部分或者是那个部分具体取决于实现方式。总之前面的读取和后面的发送都不在该线程的范围内。 Compute 进入NIO时代或者APR时代。 APR也是异步的只不过与NIO的异步方式不同。它是使用原生代码来实现的因此速度更快I/O速度也会更快。进入这个时代之后当我们考虑线程池时我们只考虑其中间部分的计算消耗。无论是在Tomcat的NIO还是APR上我们实际上无法进行优化因为当您进行数据库读写时I/O会阻塞。 对于计算部分来说它仍然会阻塞。如果想要进一步提高效率我们只能放弃Servlet这种架构转而使用Reactive Stack完全采用函数式编程。通过将与数据库交互的这些慢速操作从代码中分离出来您才有可能将这些慢速操作转换为事件响应方式。 将非慢速操作转换为可以直接在CPU上调度的方式才能进一步提高性能。 技术的发展已经很明显它是一步一步地将代码中与I/O相关的部分分离出来无论是网络I/O还是本地I/O部分。只有在将这些部分从代码中分离出来之后它才可能在多线程上进行更进一步的优化。如果不进行分离实际上是不可能的。就目前而言对于命令式编程来说最优化的方式就是采用APR。其中实际上还包括了一些本地数据库的I/O操作。由于命令式编程即Servlet堆栈并未将这部分代码分离出来。
http://www.hkea.cn/news/14379377/

相关文章:

  • 厦门区块链网站开发怎样做免费网站推广
  • 网站放音乐代码电商网站建设网
  • 怎么样让网站做的大气wordpress添加内链按钮
  • 金华网站建设多少钱昆山app网站制作
  • 优化网站公司.电子商务网站建设的核心是
  • 做节约用水海报的网站公司网站开发视频教程
  • 学校网站织梦源码深圳企业网站制作服务
  • 黄陂区建设局网站手机端店铺装修
  • 成都网站制作公司科蓝唐山免费做网站
  • 网站中的宣传册翻页动画怎么做郑州软件开发学校
  • 如何评价伊利集团网站建设网站开发招聘名称
  • 北京网站设计推荐柚米制作论坛做网站
  • joomla! 1.5 网站建设基础教程 :宁皓网软件开发工具包英文缩写
  • 建设网站怎样分配给用户空间营销手机都有什么功能啊
  • 租用的网站空间的缺点wordpress标题设置
  • 个人网站建设策划书怎么写谷歌seo排名公司
  • 家具建设企业网站怎么做阿里巴巴网站
  • 商务网站建设心得体会wordpress中设置
  • 芜湖市建设办网站中天建设集团坑人吗
  • 网站视频弹窗广告代码哪个公司的app软件定制
  • 上上佳食品 网站建设自媒体wordpress主题
  • 软件产品如何做网站推广深圳大腕互联网站建设
  • 哪里有好网站设计软件实施工资一般多少
  • 婚恋网站排名如何制作手机网页
  • wordpress怎么修改语言设置用什么程序做网站最好优化
  • 上海 网站工作室山东高阳建设公司网站
  • pa66用途障车做网站专门做娱乐场所的设计网站
  • 网站名字备案HTML可以做彩票网站吗
  • 只有一个页面的网站怎么做重庆市建设工程质量监督信息网
  • 建网站有什么用承德在线招聘