微服务基础知识点学习笔记(持续更新)

Conrtoller层

整体包括:HTTP协议,JavaWeb三大组件(filter、servlet、listener)、SpringMVC(SpringMVC的执行流程、常用配置、SpringMVC注解的使用)、OpenFeign。

HTTP协议(Hyper Text Transfer Protocol:超文本传输协议)

在Web应用中,浏览器请求一个URL,服务器就把生成的HTML网页发送给浏览器,而浏览器和服务器之间的传输协议是HTTP。而HTTP协议又是一个基于TCP/IP协议来传输数据。

常用的HTTP Header

HTTP头以Header: Value形式表示的,常用的有:

  • Host: 表示请求的主机名,因为一个服务器上可能运行着多个网站,因此,Host表示浏览器正在请求的域名;
  • User-Agent: 标识客户端本身,例如Chrome浏览器的标识类似Mozilla/5.0 ... Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...) like Gecko
  • Accept:表示浏览器能接收的资源类型,如text/*image/*或者*/*表示所有;
  • Accept-Language:表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;
  • Accept-Encoding:表示浏览器可以支持的压缩类型,例如gzip, deflate, br

HTTP常见的响应码

其中2xx表示成功,3xx表示重定向,4xx表示客户端引发的错误,5xx表示服务器端引发的错误。数字是给程序识别,文本则是给开发者调试使用的。常见的响应代码有:

  • 200 OK:表示成功;
  • 301 Moved Permanently:表示该URL已经永久重定向;
  • 302 Found:表示该URL需要临时重定向;
  • 304 Not Modified:表示该资源没有修改,客户端可以使用本地缓存的版本;
  • 400 Bad Request:表示客户端发送了一个错误的请求,例如参数无效;
  • 401 Unauthorized:表示客户端因为身份未验证而不允许访问该URL;
  • 403 Forbidden:表示服务器因为权限问题拒绝了客户端的请求;
  • 404 Not Found:表示客户端请求了一个不存在的资源;
  • 500 Internal Server Error:表示服务器处理时内部出错,例如因为无法连接数据库;
  • 503 Service Unavailable:表示服务器此刻暂时无法处理请求。

服务器经常返回的HTTP Header

  • Content-Type:表示该响应内容的类型,例如text/htmlimage/jpeg
  • Content-Length:表示该响应内容的长度(字节数);
  • Content-Encoding:表示该响应压缩算法,例如gzip
  • Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒。
通常浏览器获取的第一个资源是HTML网页,在网页中,如果嵌入了JavaScript、CSS、图片、视频等其他资源,浏览器会根据资源的URL再次向服务器请求对应的资源。HTTP是以客户端的身份去请求服务器资源,以服务器的身份响应客户端请求,同时编写服务器程序来处理客户端请求通常就称之为Web开发。

Servlet

简介

在JavaEE平台上,处理TCP连接,解析HTTP协议这些底层工作统统扔给现成的Web服务器去做,我们只需要把自己的应用程序跑在Web服务器上。为了实现这一目的,JavaEE提供了Servlet API,使用Servlet API编写自己的Servlet来处理HTTP请求,Web服务器实现Servlet API接口,实现底层功能:

                 ┌───────────┐│My Servlet │├───────────┤│Servlet API│
┌───────┐  HTTP  ├───────────┤
│Browser│<──────>│Web Server │
└───────┘        └───────────┘

一个Servlet总是继承自HttpServlet,然后覆写doGet()doPost()方法。注意到doGet()方法传入了HttpServletRequestHttpServletResponse两个对象,分别代表HTTP请求和响应。我们使用Servlet API时,并不直接与底层TCP交互,也不需要解析HTTP协议,因为HttpServletRequestHttpServletResponse就已经封装好了请求和响应。

同时,我们编写的Servlet并不是直接运行,而是由Web服务器加载后创建实例运行,所以,类似Tomcat这样的Web服务器也称为Servlet容器

在Servlet容器中运行的Servlet具有如下特点:

  • 无法在代码中直接通过new创建Servlet实例,必须由Servlet容器自动创建Servlet实例;
  • Servlet容器只会给每个Servlet类创建唯一实例;
  • Servlet容器会使用多线程执行doGet()doPost()方法。

结合多线程可得出:

  • 在Servlet中定义的实例变量会被多个线程同时访问,要注意线程安全;
  • HttpServletRequestHttpServletResponse实例是由Servlet容器传入的局部变量,它们只能被当前线程访问,不存在多个线程访问的问题;
  • doGet()doPost()方法中,如果使用了ThreadLocal,但没有清理,那么它的状态很可能会影响到下次的某个请求,因为Servlet容器很可能用线程池实现线程复用。

因此编写Servlet必须考虑多线程同步,需要同步的必须要同步。

多servlet的WebApp

  • 一个WebApp是由多个Servlet组成,每个servlet类似于MVC中的控制器可以通过注解来表示自己可以处理的请求路径:

    @WebServlet(urlPatterns = "/hello")
    public class HelloServlet extends HttpServlet {...
    }
    

    上述HelloServlet能处理/hello这个路径的请求。

  • 因为浏览器发送请求的时候,还会有请求方法(HTTP Method):即GET、POST、PUT等不同类型的请求。因此,要处理GET请求,我们要覆写doGet()方法:

    @WebServlet(urlPatterns = "/hello")
    public class HelloServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {...}
    }
    

    类似的,要处理POST请求,就需要覆写doPost()方法。否则就会直接返回报错。

HttpServletRequest

HttpServletRequest封装了一个HTTP请求,它实际上是从ServletRequest继承而来。最早设计Servlet时,设计者希望Servlet不仅能处理HTTP,也能处理类似SMTP等其他协议,因此,单独抽出了ServletRequest接口,但实际上除了HTTP外,并没有其他协议会用Servlet处理,所以这是一个过度设计。

我们通过HttpServletRequest提供的接口方法可以拿到HTTP请求的几乎全部信息,常用的方法有:

  • getMethod():返回请求方法,例如,"GET""POST"
  • getRequestURI():返回请求路径,但不包括请求参数,例如,"/hello"
  • getQueryString():返回请求参数,例如,"name=Bob&a=1&b=2"
  • getParameter(name):返回请求参数,GET请求从URL读取参数,POST请求从Body中读取参数;
  • getContentType():获取请求Body的类型,例如,"application/x-www-form-urlencoded"
  • getContextPath():获取当前Webapp挂载的路径,对于ROOT来说,总是返回空字符串""
  • getCookies():返回请求携带的所有Cookie;
  • getHeader(name):获取指定的Header,对Header名称不区分大小写;
  • getHeaderNames():返回所有Header名称;
  • getInputStream():如果该请求带有HTTP Body,该方法将打开一个输入流用于读取Body;
  • getReader():和getInputStream()类似,但打开的是Reader;
  • getRemoteAddr():返回客户端的IP地址;
  • getScheme():返回协议类型,例如,"http""https"

此外,HttpServletRequest还有两个方法:setAttribute()getAttribute(),可以给当前HttpServletRequest对象附加多个Key-Value,相当于把HttpServletRequest当作一个Map<String, Object>使用。

调用HttpServletRequest的方法时,注意务必阅读接口方法的文档说明,因为有的方法会返回null,例如getQueryString()的文档就写了:

... This method returns null if the URL does not have a query string...

HttpServletResponse

HttpServletResponse封装了一个HTTP响应。由于HTTP响应必须先发送Header,再发送Body,所以,操作HttpServletResponse对象时,必须先调用设置Header的方法,最后调用发送Body的方法。

常用的设置Header的方法有:

  • setStatus(sc):设置响应代码,默认是200
  • setContentType(type):设置Body的类型,例如,"text/html"
  • setCharacterEncoding(charset):设置字符编码,例如,"UTF-8"
  • setHeader(name, value):设置一个Header的值;
  • addCookie(cookie):给响应添加一个Cookie;
  • addHeader(name, value):给响应添加一个Header,因为HTTP协议允许有多个相同的Header;

写入响应时,需要通过getOutputStream()获取写入流,或者通过getWriter()获取字符流,二者只能获取其中一个。

写入响应前,无需设置setContentLength(),因为底层服务器会根据写入的字节数自动设置,如果写入的数据量很小,实际上会先写入缓冲区,如果写入的数据量很大,服务器会自动采用Chunked编码让浏览器能识别数据结束符而不需要设置Content-Length头。

但是,写入完毕后调用flush()却是必须的,因为大部分Web服务器都基于HTTP/1.1协议,会复用TCP连接。如果没有调用flush(),将导致缓冲区的内容无法及时发送到客户端。此外,写入完毕后千万不要调用close(),原因同样是因为会复用TCP连接,如果关闭写入流,将关闭TCP连接,使得Web服务器无法复用此TCP连接。

写入完毕后对输出流调用flush()而不是close()方法!

有了HttpServletRequestHttpServletResponse这两个高级接口,我们就不需要直接处理HTTP协议。注意到具体的实现类是由各服务器提供的,而我们编写的Web应用程序只关心接口方法,并不需要关心具体实现的子类。

Servlet多线程模型

一个Servlet类在服务器中只有一个实例,但对于每个HTTP请求,Web服务器会使用多线程执行请求。因此,一个Servlet的doGet()doPost()等处理请求的方法是多线程并发执行的。如果Servlet中定义了字段,要注意多线程并发访问的问题:

public class HelloServlet extends HttpServlet {private Map<String, String> map = new ConcurrentHashMap<>();protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 注意读写map字段是多线程并发的:this.map.put(key, value);}
}

对于每个请求,Web服务器会创建唯一的HttpServletRequestHttpServletResponse实例,因此,HttpServletRequestHttpServletResponse实例只有在当前处理线程中有效,它们总是局部变量,不存在多线程共享的问题。

重定向与转发

重定向Redirect:

重定向是指浏览器请求一个URL时,服务器返回一个重定向指令,需要浏览器用新的URL重新发出请求。这个过程就是302响应。如图所示:

┌───────┐   GET /hi     ┌───────────────┐
│Browser│ ────────────> │RedirectServlet│          第一次发出GET /hi请求,服务器返回重定向302相应并给予新的URL。
│       │ <──────────── │               │
└───────┘   302         └───────────────┘┌───────┐  GET /hello   ┌───────────────┐
│Browser│ ────────────> │ HelloServlet  │          第二次请求使用新的GET /hello请求,成功获取<html>资源。
│       │ <──────────── │               │
└───────┘   200 <html>  └───────────────┘

转发Forward:

转发是指在服务器内部进行重新调度。如图所示:

                          ┌────────────────────────┐│      ┌───────────────┐ ││ ────>│ForwardServlet │ │
┌───────┐  GET /morning   │      └───────────────┘ │
│Browser│ ──────────────> │              │         │
│       │ <────────────── │              ▼         │
└───────┘    200 <html>   │      ┌───────────────┐ ││ <────│ HelloServlet  │ ││      └───────────────┘ ││       Web Server       │└────────────────────────┘

ServletContext

什么是ServletContext

要理解ServletContext就必须和Cookie、Session做一个对比,如下图:

你可以把它想象成一个公用的空间,可以被所有的客户访问,也就是说A客户端可以访问D,B客户端可以访问D,C客户端也可以访问D。

总结可知:

  • WEB容器在启动时,它会为每个Web应用程序都创建一个对应的ServletContext,它代表当前Web应用。并且它被所有客户端共享。
  • ServletContext对象可以通过ServletConfig.getServletContext()方法获得对ServletContext对象的引用,也可以通过this.getServletContext()方法获得其对象的引用。
  • 由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。
  • 当web应用关闭、Tomcat关闭或者Web应用reload的时候,ServletContext对象会被销毁
使用ServletContext

(1) 如何得到ServletContext对象

this.getServletContext();
this.getServletConfig().getServletContext();

(2) 你可以把它想象成一张表,这个和Session非常相似:每一行就是一个属性,如下:

名字(String) 值(Object)
  • 添加属性:setAttribute(String name, Object obj);
  • 得到值: getAttribute(String name),这个方法返回Object
  • 删除属性:removeAttribute(String name)

(3) 生命周期

ServletContext中的属性的生命周期从创建开始,到服务器关闭结束。

ServletContext是存在于服务器内存中的一个公共空间,除非关闭或重启服务器才会失去其中的数据。

使用Session和Cookie

因为HTTP是无状态协议,Web应用程序无法识别两次HTTP请求是否为同一个浏览器发出。所以需要使用Session和Cookie实现这一机制。

Session

浏览器第一次访问服务器的时候会被分配一个唯一的Session ID,如果用户在一段时间内没有访问服务器,那么Session会自动失效,下次即使带着上次分配的Session ID访问,服务器也认为这是一个新用户,会分配新的Session ID。

我们可以在session里面保存用户登录的信息,再次访问servlet时可以尝试在session中找到已经登录过的用户,可以实现识别不同的HTTP请求是否为用一个浏览器发出。所有的servlet都在维护同一个session(相同的内存空间)如图:

           ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐│      ┌───────────────┐                │| ┌───>│ IndexServlet  │<──────────┐    |│ │    └───────────────┘           ▼    │
┌───────┐  | │    ┌───────────────┐      ┌────────┐|
│Browser│──┼─┼───>│ SignInServlet │<────>│Sessions││
└───────┘  | │    └───────────────┘      └────────┘|│ │    ┌───────────────┐           ▲    │| └───>│SignOutServlet │<──────────┘    |│      └───────────────┘                │└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
Cookie

在当前浏览器请求的servlet第一次调用getSession()时,就会创建一个SessionID,然后通过JSESSIONID的Cookie发送给浏览器。

Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。Java Servlet 显然支持 HTTP Cookie。

识别返回用户包括三个步骤:

  • 服务器脚本向浏览器发送一组 Cookie。例如:姓名、年龄或识别号码等。
  • 浏览器将这些信息存储在本地计算机上,以备将来使用。
  • 当下一次浏览器向 Web 服务器发送任何请求时,浏览器会把这些 Cookie 信息发送到服务器,服务器将使用这些信息来识别用户。

Filter

在一个比较复杂的Web应用程序中,通常都有很多URL映射,对应的,也会有多个Servlet来处理URL。为了把一些公用逻辑从各个Servlet中抽离出来,JavaEE的Servlet规范还提供了一种Filter组件,即过滤器,它的作用是,在HTTP请求到达Servlet之前,可以被一个或多个Filter预处理,类似打印日志、登录检查等逻辑,完全可以放到Filter中。
  • 编写方法:

编写Filter时,必须实现Filter接口,在doFilter()方法内部,要继续处理请求,必须调用chain.doFilter()。最后,用@WebFilter注解标注该Filter需要过滤的URL。这里的/*表示所有路径。如果一定要给每个Filter指定顺序,就必须在web.xml文件中对这些Filter再配置一遍。

例如,我们编写一个最简单的EncodingFilter,它强制把输入和输出的编码设置为UTF-8:

@WebFilter(urlPatterns = "/*")
public class EncodingFilter implements Filter {public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("EncodingFilter:doFilter");request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");chain.doFilter(request, response);}
}

再写一个针对特殊路径的过滤器:

@WebFilter("/user/*")
public class AuthFilter implements Filter {public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {System.out.println("AuthFilter: check authentication");HttpServletRequest req = (HttpServletRequest) request;HttpServletResponse resp = (HttpServletResponse) response;if (req.getSession().getAttribute("user") == null) {// 未登录,自动跳转到登录页:System.out.println("AuthFilter: not signin!");resp.sendRedirect("/signin");} else {// 已登录,继续处理:chain.doFilter(request, response);}}
}

​ 注意到AuthFilter只过滤以/user/开头的路径,因此:

  • 如果一个请求路径类似/user/profile,那么它会被上述3个Filter依次处理;
  • 如果一个请求路径类似/test,那么它会被上述2个Filter依次处理(不会被AuthFilter处理)。

再注意观察AuthFilter,当用户没有登录时,在AuthFilter内部,直接调用resp.sendRedirect()发送重定向,且没有调用chain.doFilter(),因此,当用户没有登录时,请求到达AuthFilter后,不再继续处理,即后续的Filter和任何Servlet都没有机会处理该请求了。

可见,Filter可以有针对性地拦截或者放行HTTP请求。

再次提醒! 如果Filter要使请求继续被处理,就一定要调用chain.doFilter()!


SpringMVC

具体内容转到我的Spring知识点总结博客

微服务基础知识点学习笔记(持续更新)相关推荐

  1. 重拾CCNA,学习笔记持续更新ing......(4)

    重拾CCNA,学习笔记持续更新ing......(4) 路由器作用功能的经典解说(笑)(非原创) 假设你的名字叫小不点,你住在一个大院子里,你的邻居有很多小伙伴,在门口传达室还有个看大门的李大爷,李大 ...

  2. 《SpringCloud微服务架构》学习笔记

    一.SpringCloud概述 说到SpringCloud,相信大家都不陌生,它主要是用来管理微服务的,说直白有点,它就是基于SpringBoot实现的一套微服务治理工具包,它并不是一个框架,而是一系 ...

  3. Admin.NET管理系统(vue3等前后端分离)学习笔记--持续更新

    我的学习笔记 - 9iAdmin.NET 欢迎学习交流 (一)前端笔记 1.1 关于.env的设置 1.2 关于路由模式问题 1.3 关于 vue.config.ts 1.4 关于 打包(pnpm r ...

  4. 记微服务架构实战学习笔记

    架构演进和分布式系统基础知识 1.传统架构演进到分布式架构 简介:讲解单机应用和分布式应用架构演进基础知识 (画图) 高可用 LVS+keepalive 1.单体应用:开发速度慢启动时间长依赖庞大等等 ...

  5. 阿里巴巴微服务开源项目盘点(持续更新)

    大前端.微服务.数据库.更多精彩,尽在开发者分会场 [Apache Dubbo] Apache Dubbo 是一款高性能.轻量级的开源Java RPC框架,是国内影响力最大.使用最广泛的开源服务框架之 ...

  6. 专升本 计算机 公共课学习笔记(持续更新中...)

    计算机公共课学习笔记 第一章 计算机基础知识(30分) 1.计算机概述 计算机(Computer)的起源与发展 计算机(Computer)也称"电脑",是一种具有计算功能.记忆功能 ...

  7. JS逆向学习笔记 - 持续更新中

    JS逆向学习笔记 寻找深圳爬虫工作,微信:cjh-18888 文章目录 JS逆向学习笔记 一. JS Hook 1. JS HOOK 原理和作用 原理:替换原来的方法. (好像写了句废话) 作用: 可 ...

  8. typescript-----javascript的超集,typescript学习笔记持续更新中......

    Typescript,冲! Typescript 不是一门全新的语言,Typescript是 JavaScript 的超集,它对 JavaScript进行了一些规范和补充.使代码更加严谨. 一个特别好 ...

  9. 计算机网络:学习笔记(持续更新)

    文章目录 前言 1.1 计算机网络基本概念 什么是计算机网络? 什么是网络协议? 1.2 计算机网络结构 计算机网络结构 网络边缘 接入网络(物理介质) 网络核心(核心网络) Internet结构 1 ...

最新文章

  1. WordPress中的cookie 机制
  2. SAP成都研究院C4C光明左使:SAP Cloud for Customer 使用SAP UI5的独特之处
  3. 你确定你真的理解“双亲委派“了吗?!
  4. 部署Vista需要了解的十大事项
  5. 从19本书中选取五本,并且要求这五本互相不相邻,一共有多少种方法?
  6. 恐怖黎明 联网显示无法连接服务器,豪横的刷刷刷 2021年最值得体验的暗黑类游戏...
  7. centos7设置静态IP地址方法
  8. [转]Cryengine渲染引擎剖析
  9. 计算机格式化没有fat32,windows里面没有FAT32格式化命令
  10. 小迪-65-内网安全
  11. 同城跑腿微信小程序制作步骤_分享下同城跑腿小程序的作用
  12. 金山打字通83字/分
  13. 35,UC(14) .
  14. 无法访问您试图使用的功能所在的网络位置 无法找到vcredist.msi的解决办法
  15. 基于非结构化的数据管理探究
  16. 爬百度贴吧并保存链接
  17. Java-正则表达式:匹配特定字符开头,数字结尾的任一字符串
  18. 李家同《让高墙倒下吧》
  19. 【高保真原型制作】上海道宁为您带来适用于所有数字产品的简单的​交互式原型制作工具——ProtoPie
  20. 基于AndroidJava的食谱菜谱菜品APP设计

热门文章

  1. 劳动保障职业学院计算机专业,北京劳动保障职业学院2020录取分数线(附2017-2020年分数线)...
  2. 聊一聊推荐系统中ExploitExplore算法
  3. SSH密码暴力破解及防御实战
  4. 论开学第三个月干了点啥
  5. IntelliJ IDEA使用教程(动图详解):实时代码模板的使用
  6. 制作 macOS High Sierra U盘
  7. 机器学习中SVM的损失函数,向量积
  8. h5调用支付宝 php支付源码,友价源码如何集成支付宝H5接口(即支付宝手机支付接口)...
  9. 4个月学前端找不到工作正常吗?什么原因?
  10. 《零基础学C语言》前言