lagou 爪哇 2-1 tomcat / nginx 笔记

简介: Tomcat 系统架构与原理剖析注意:浏览器访问服务器使⽤的是Http协议,Http是应⽤层协议,⽤于定义数据通信的格式,具体的数据传输使⽤的是TCP/IP协议Tomcat 系统总体架构Tomcat是⼀个Http服务器(能够接收并且处理http请求,所以tomcat是⼀个http服务器)我们使⽤浏览器向某⼀个⽹站发起请求,发出的是Http请求,那么在远程,Http服务器接收到这个请求之后,会调⽤具体的程序(Java类)进⾏处理,往往不同的请求由不同的Java类完成处理。Tomcat 设计了两个核⼼组件连接器(Connector)和容器(Container)来完成 Tomcat 的两⼤核

Tomcat 系统架构与原理剖析


image.png


注意:浏览器访问服务器使⽤的是Http协议,Http是应⽤层协议,⽤于定义数据通信的格式,具体的数据传输使⽤的是TCP/IP协议


Tomcat 系统总体架构


Tomcat是⼀个Http服务器(能够接收并且处理http请求,所以tomcat是⼀个http服务器)我们使⽤浏览器向某⼀个⽹站发起请求,发出的是Http请求,那么在远程,Http服务器接收到这个请求之后,会调⽤具体的程序(Java类)进⾏处理,往往不同的请求由不同的Java类完成处理。


image.png


Tomcat 设计了两个核⼼组件连接器(Connector)和容器(Container)来完成 Tomcat 的两⼤核⼼功能。


连接器,负责对外交流: 处理Socket连接,负责⽹络字节流与Request和Response对象的转化;


容器,负责内部处理:加载和管理Servlet,以及具体处理Request请求;


Tomcat 连接器组件 Coyote


Coyote 简介


Coyote 是Tomcat 中连接器的组件名称 , 是对外的接⼝。客户端通过Coyote与服务器建⽴连接、发送请


求并接受响应 。


(1)Coyote 封装了底层的⽹络通信(Socket 请求及响应处理)


(2)Coyote 使Catalina 容器(容器组件)与具体的请求协议及IO操作⽅式完全解耦


(3)Coyote 将Socket 输⼊转换封装为 Request 对象,进⼀步封装后交由Catalina 容器进⾏处理,处

理请求完成后, Catalina 通过Coyote 提供的Response 对象将结果写⼊输出流


(4)Coyote 负责的是具体协议(应⽤层)和IO(传输层)相关内容


image.png

在 8.0 之前 ,Tomcat 默认采⽤的I/O⽅式为 BIO,之后改为 NIO。 ⽆论 NIO、NIO2 还是 APR, 在性能⽅⾯均优于以往的BIO。 如果采⽤APR, 甚⾄可以达到 Apache HTTP Server 的影响性能。


Tomcat 服务器核⼼配置详解



⼿写实现迷你版 Tomcat


Tomcat 源码构建及核⼼流程源码剖析


Tomcat 类加载机制剖析


Tomcat 对 Https 的⽀持及 Tomcat 性能优化策略


nginx 相关


Nginx基础回顾(Nginx是什么?能做什么事情(应⽤在什么场合)?常⽤命令是什么?)


正向代理


在浏览器中配置代理服务器的相关信息,通过代理服务器访问⽬标⽹站,代理服务器收

到⽬标⽹站的响应之后,会把响应信息返回给我们⾃⼰的浏览器客户端


反向代理


浏览器客户端发送请求到反向代理服务器(⽐如 Nginx),由反向代理服务器选择原始

服务器提供服务获取结果响应,最终再返回给客户端浏览器


负载均衡服务器


负载均衡,当⼀个请求到来的时候(结合上图),Nginx反向代理服务器根据请求去找到⼀个原始服务器来处理当前请求,那么这叫做反向代理。那么,如果⽬标服务器有多台(⽐如上图中的tomcat1,tomcat2,tomcat3...),找哪⼀个⽬标服务器来处理当前请求呢,这样⼀个寻找确定的过程就叫做负载均衡。


⽣活中也有很多这样的例⼦,⽐如,我们去银⾏,可以处理业务的窗⼝有多个,那么我们会被分配到哪个窗⼝呢到底,这样的⼀个过程就叫做负载均衡。


Nginx 核⼼配置⽂件解读


Nginx的核⼼配置⽂件 conf/nginx.conf 包含三块内容:全局块、events块、http块

全局块


从配置⽂件开始到 events 块之间的内容,此处的配置影响nginx服务器整体的运⾏,⽐如worker进


程的数量、错误⽇志的位置等


event 模块


events块主要影响 nginx 服务器与⽤户的⽹络连接,⽐如 worker_connections 1024,标识每个 workderprocess ⽀持的最⼤连接数为1024


http 模块


http块是配置最频繁的部分,虚拟主机的配置,监听端⼝的配置,请求转发、反向代理、负载均衡等


Nginx应⽤场景之反向代理


image.png

再部署⼀台tomcat,保持默认监听8081端⼝


修改nginx配置,并重新加载

image.png

这⾥主要就是多location的使⽤,这⾥的nginx中server/location就好⽐tomcat中的

Host/Context location 语法如下:


在nginx配置⽂件中,location主要有这⼏种形式(优先级由高到低):


  1. 精确匹配 location = /lagou { }


  1. 匹配路径的前缀 location ^~ /lagou { }


  1. 不区分⼤⼩写的正则匹配 location ~* /lagou { }


  1. 正则匹配 location ~ /lagou { }


  1. 普通路径前缀匹配 location /lagou { }


Nginx应⽤场景之负载均衡


第一步是定义 upstream, 起一个名字. 然后再使用 proxy_pass 即可.

Nginx负载均衡策略:


  1. 轮询


 默认策略,每个请求按时间顺序逐⼀分配到不同的服务器,如果某⼀个服务器下线,能    ⾃动剔除

location /abc {
 proxy_pass http://myServer/;
}
upstream myServer{
 server 111.229.248.243:8080;
 server 111.229.248.243:8082;
}


  1. 权重 weight


    weight代表权重,默认每⼀个负载的服务器都为1,权重越⾼那么被分配的请求越多  ( ⽤于服务器性能不均衡的场景)

upstream myServer{
 server 111.229.248.243:8080 weight=1;
 server 111.229.248.243:8082 weight=2;
}


  1. ip_hash



每个请求按照ip的hash结果分配,每⼀个客户端的请求会固定分配到同⼀个⽬标服务器处理,可以解决session问题


第五部分 Nginx应⽤场景之动静分离

upstream myServer{
 ip_hash;
 server 111.229.248.243:8080;
 server 111.229.248.243:8082;
}


Nginx 应⽤场景之动静分离


动静分离就是讲动态资源和静态资源的请求处理分配到不同的服务器上,⽐较经典的组合就是


Nginx+Tomcat架构(Nginx处理静态资源请求,Tomcat处理动态资源请求)


image.png


Nginx 底层进程机制剖析


Nginx启动后,以daemon多进程⽅式在后台运⾏,包括⼀个Master进程和多个Worker进程,Master 进程是领导,是⽼⼤,Worker进程是⼲活的⼩弟。


master进程


主要是管理worker进程,⽐如:


接收外界信号向各worker进程发送信号(./nginx -s reload)


监控worker进程的运⾏状态,当worker进程异常退出后Master进程会⾃动重新启动新的worker进程等


worker进程


worker进程具体处理⽹络请求。多个worker进程之间是对等的,他们同等竞争来⾃客户端的请求,各进程互相之间是独⽴的。⼀个请求,只可能在⼀个worker进程中处理,⼀个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的,⼀般设置与机器cpu核数⼀致。


Nginx进程模型示意图如下


image.png

以 ./nginx -s reload 来说明nginx信号处理这部分


1)master进程对配置⽂件进⾏语法检查


2)尝试配置(⽐如修改了监听端⼝,那就尝试分配新的监听端⼝)


3)尝试成功则使⽤新的配置,新建worker进程


4)新建成功,给旧的worker进程发送关闭消息


5)旧的worker进程收到信号会继续服务,直到把当前进程接收到的请求处理完毕后关闭

所以reload之后worker进程pid是发⽣了变化的


worker进程处理请求部分的说明


例如,我们监听9003端⼝,⼀个请求到来时,如果有多个worker进程,那么每个worker进程都有可能处理这个链接。


master进程创建之后,会建⽴好需要监听的的socket,然后从master进程再fork出多个worker进程。所以,所有worker进程的监听描述符listenfd在新连接到来时都变得可读。

nginx使⽤互斥锁来保证只有⼀个workder进程能够处理请求,拿到互斥锁的那个进程注册 listenfd 读事件,在读事件⾥调⽤accept接受该连接,然后解析、处理、返回客户端

nginx多进程模型好处


每个worker进程都是独⽴的,不需要加锁,节省开销


每个worker进程都是独⽴的,互不影响,⼀个异常结束,其他的照样能提供服务


多进程模型为reload热部署机制提供了⽀撑


其他


URL 编码和解码问题


http://www.ruanyifeng.com/blog/2010/02/url_encoding.html

作业


作业⼀(编程题):


开发Minicat V4.0,在已有Minicat基础上进⼀步扩展,模拟出webapps部署效果 磁盘上放置⼀个webapps⽬录,webapps中可以有多个项⽬,⽐如demo1,demo2,demo3... 具体的项⽬⽐如demo1中有serlvet(也即为:servlet是属于具体某⼀个项⽬的servlet),这样的话在 Minicat初始化配置加载,以及根据请求url查找对应serlvet时都需要进⼀步处理


!!!重要


备注读取项目磁盘统一路径: ****appBase="/Users/webapps",并且提交自己的webapps以及访问路径****


作业⼆(简答题): 请详细描述Tomcat体系结构(图⽂并茂)


作业具体要求参考以下链接文档:


https://gitee.com/lagouedu/alltestfile/raw/master/tomcat/Tomcat%E4%BD%9C%E4%B8%9A%E5%A4%A7%E9%A2%98.pdf


作业资料说明:

1、提供资料:工程代码和自己的webapps以及访问路径、功能演示和原理讲解视频,简答题资料。

2、讲解内容包含:题目分析、实现思路、代码讲解。

3、效果视频验证:实现模拟tomcat多项目部署效果,访问多个项目获得动态返回的内容。


  1. 增加server.xml配置⽂件,server.xml保持基本结构即可,如下

<?xml version="1.0" encoding="utf-8" ?>
<Server>
    <services>
        <!--监听端口号-->
        <Connector port="80"/>
        <Engine>
            <!--appBase 为部署目录-->
            <Host name="localhost" appBase="C:/Users/hp/Desktop/第二阶段模块1/第二阶段模块1/code/myWebapps" />
        </Engine>
    </services>
</Server>


  1. 制作 demo1 和 demo2 目录



demo1/

index.html
edu/lagou/server/LagouServlet01.class
edu/lagou/server/LagouServlet02.class
web.xml


demo2/

index.html
edu/lagou/server/LagouServlet03.class
web.xml


  1. 在 Bootstrap 类中添加 loadAppBase 方法,可得到 port 和 appBase的值.

简易的 Mapper  类

public class Mapper {
    /**
     * 一级目录 以及 对应的动态资源
     */
    private Map<String, Map<String, HttpServlet>> content2Wrapper = new HashMap<>();
    /**
     * 一级目录 以及 对应的相对路径
     */
    private Map<String, String> resourcesMap = new HashMap<>();
    public void putStaticResources(String key, String value) {
        resourcesMap.put("/" + key, value);
    }
    public void putServletMap(String key, Map<String, HttpServlet> value) {
        content2Wrapper.put("/" + key, value);
    }
    public Map<String, HttpServlet> getServletMap(String key) {
        return content2Wrapper.get(key);
    }
    public String getStaticPath(String key) {
        return resourcesMap.get(key);
    }
}


自定义的 ClassLoader 类: 用于加载特定目录的 class

public class MyClassLoader extends ClassLoader {
    public MyClassLoader(String basePath) {
        this.basePath = basePath;
    }
    /**
     * @description 解析类文件获得当前解析后的类
     * @param fullClassName 档期类的完全限定名, 例如 edu.lagou.server.my.Person
     * @return 使用类加载器后获取的类
     * @throws Exception 解析错误
     */
    public Class<?> transClassFile(String fullClassName) throws Exception {
        byte[] classBytes = this.loadBinaryClassFile(fullClassName);
        //主要通过父类来解析当前的class二进制文件
        return super.defineClass(fullClassName, classBytes, 0, classBytes.length);
    }
    /**
     * @description 读取并加载类文件获得byte数组返回
     * @return byte[] 数组
     * @throws Exception 读取失败
     */
    private byte[] loadBinaryClassFile(String packageName) throws Exception {
        String relativePath = packageName.replaceAll("\\.", "/");
        String classFilePath = this.basePath + "/" +relativePath + ".class";// 设置当前class文件的路径
        File classFile = new File(classFilePath);
        if (!classFile.exists()) {
            throw new FileNotFoundException(classFile.getAbsolutePath() + "文件不存在。。。。。。。。。。");
        }
        InputStream fis = null;
        ByteArrayOutputStream bos = null;// 内存流
        byte[] bytes = new byte[(int) classFile.length()]; // 设置缓冲区
        byte[] readBytes = null;
        try {
            bos = new ByteArrayOutputStream();
            // 开始实例化流,并加载流
            fis = new FileInputStream(classFile);// 这里必须为文件的实际路径
            while (( fis.read(bytes)) != -1) {
                bos.write(bytes);
            }
            readBytes = bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                fis.close();
            }
            if (bos != null) {
                bos.close();
            }
        }
        return readBytes;
    }
    private final String basePath;
}


创建 loadServerXML 方法,将 server.xml 加载到内存

private void loadServerXML() {
        InputStream inputStream = Bootstrap.class.getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();
            Node portNode = rootElement.selectSingleNode("/Server/services/Connector/@port");
            this.port = Integer.parseInt(portNode.getStringValue());
            System.out.println("配置 port = " + this.port);
            Node appBaseNode = rootElement.selectSingleNode("/Server/services/Engine/Host/@appBase");
            this.appBase = appBaseNode.getStringValue();
            System.out.println("配置 appBase = " + this.appBase);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


加载 appBase 目录下各项目 以及 对应的 url-pattern和HttpServlet 动态资源 和 静态资源

private void loadAppBaseServlet() {
        File file = new File(this.appBase);
        for (File f : Objects.requireNonNull(file.listFiles())) {
            if (f.isDirectory()) {
                // 解析 web.xml 并加载对应的 HttpServlet 类
                String webXmlFileName = f.getAbsolutePath() + "/" + "web.xml";
                File webXmlFile = new File(webXmlFileName);
                // 如果连 web.xml 不存在 则跳过该目录。
                if (webXmlFile.exists()) {
                    String fileName = f.getName();
                    System.out.println(fileName);
                    System.out.println("--------------");
                    System.out.println("...加载该目录" + f.getPath());
                    System.out.println(webXmlFileName);
                    // 加载对应的 各项目父路径 和 对应的 url-patten与servlet动态资源的对应关系, 例如 /demo1 -> (/lagou -> LagouServlet对象)
                    try {
                        InputStream inputStream = new FileInputStream(webXmlFile);
                        Map<String, HttpServlet> stringHttpServletMap = this.loadServletFromInputStream(inputStream,
                                new MyClassLoader(f.getAbsolutePath()));
                        this.mapper.putServletMap(fileName, stringHttpServletMap);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    }
                    // 加载对应的 各项目父路径 和 对应静态资源父目录的对应关系, 例如 /demo1 -> appBase/demo1
                    this.mapper.putStaticResources(fileName, f.getAbsolutePath());
                } else {
                    System.out.println("...跳过该目录" + f.getPath());
                }
            }
        }
    }


RequestProcessor 的 run 方法

// url
            String url = request.getUrl();
            // 截取两端/index.html    / + index.html
            // /demo1/lagou
            String[] split = url.split("/");
            String part1;
            String part2;
            if (split.length == 2) {
                part1 = "/" + split[0];
                part2 = "/" + split[1];
            } else {
                part1 = "/" + split[1];
                part2 = "/" + split[2];
            }
            Map<String, HttpServlet> stringHttpServletMap = this.mapper.getServletMap(part1);
            if (stringHttpServletMap != null) {
                HttpServlet httpServlet = stringHttpServletMap.get(part2);
                // process static resources
                if (null == httpServlet) {
                    String filePath = this.mapper.getStaticPath(part1);
                    response.outputHtml(filePath, part2);
                } else {
                    httpServlet.service(request, response);
                }
            } else {
                // 404
                response.outputHtml("NOT FOUND");
            }


思路:Mapper类—>Host->Context->Wrapper->Servlet


开始测试


正面案例


http://localhost/index.html


http://localhost/lagou


http://localhost/demo1/index.html


http://localhost/demo1/lagou001


http://localhost/demo1/lagou002


http://localhost/demo2/index.html


http://localhost/demo2/lagou333


反面案例


http://localhost/demo1/abc.html


http://localhost/demo2/lagou003


其中遇到的问题


StaticResourceUtil#getAbsolutePath


如果包含特殊字符,需要进行一次 URL 解码工作


String resourcePath = StaticResourceUtil.class.getResource("/").getPath();
        System.out.println(resourcePath);
        // 如果包含特殊字符,需要进行一次 URL 解码工作
        try {
            resourcePath = java.net.URLDecoder.decode(resourcePath, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }


作业2 解答

image.png

Tomcat 连接器组件 Coyote


Coyote 是Tomcat 中连接器的组件名称 , 是对外的接⼝。客户端通过Coyote与服务器建⽴连接、发送请


求并接受响应 。


(1)Coyote 封装了底层的⽹络通信(Socket 请求及响应处理)


(2)Coyote 使Catalina 容器(容器组件)与具体的请求协议及IO操作⽅式完全解耦


(3)Coyote 将Socket 输⼊转换封装为 Request 对象,进⼀步封装后交由Catalina 容器进⾏处理,处


理请求完成后, Catalina 通过Coyote 提供的Response 对象将结果写⼊输出流

Coyote 负责的是具体协议(应⽤层)和IO

image.png

image.png


即一个由 Server->Service->Engine->Host->Context 组成的结构,从里层向外层分别是:


Server:服务器Tomcat的顶级元素,它包含了所有东西。


Service:一组 Engine(引擎) 的集合,包括线程池 Executor 和连接器 Connector 的定义。


Engine(引擎):一个 Engine代表一个完整的 Servlet 引擎,它接收来自Connector的请求,并决定传给哪个Host来处理。


Container(容器):Host、Context、Engine和Wraper都继承自Container接口,它们都是容器。


Connector(连接器):将Service和Container连接起来,注册到一个Service,把来自客户端的请求转发到Container。


Host:即虚拟主机,所谓的”一个虚拟主机”可简单理解为”一个网站”。


Context(上下文 ): 即 Web 应用程序,一个 Context 即对于一个 Web 应用程序。


Context容器直接管理Servlet的运行,Servlet会被其给包装成一个StandardWrapper类去运行。


Wrapper负责管理一个Servlet的装载、初始化、执行以及资源回收,它是最底层容器。

Container包含以下结构


Engine


表示整个Catalina的Servlet引擎,⽤来管理多个虚拟站点,⼀个Service最多只能有⼀个Engine,   但是⼀个引擎可包含多个Host


Host


代表⼀个虚拟主机,或者说⼀个站点,可以给Tomcat配置多个虚拟主机地址,⽽⼀个虚拟主机下可  包含多个Context


Context


表示⼀个Web应⽤程序, ⼀个Web应⽤可包含多个Wrapper


Wrapper


表示⼀个Servlet,Wrapper


image.png


具体请求流程




相关实践学习
部署高可用架构
本场景主要介绍如何使用云服务器ECS、负载均衡SLB、云数据库RDS和数据传输服务产品来部署多可用区高可用架构。
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
2月前
|
缓存 前端开发 JavaScript
tomcat核心技术+Nginx性能调优技术
而Tomcat的基本配置,每个配置项也基本上对应了Tomcat的组件结构,如果要用一张图来形象展现一下Tomcat组成的话
37 1
|
2月前
|
Java 应用服务中间件 Apache
简介Nginx,Tomcat和 Apache
简介Nginx,Tomcat和 Apache
简介Nginx,Tomcat和 Apache
|
3月前
|
存储 缓存 负载均衡
Nginx入门笔记
Nginx入门笔记
111 0
|
3月前
|
负载均衡 应用服务中间件 nginx
百度搜索:蓝易云【Nginx和tomcat实现负载均衡教程】
至此,你已经成功地使用Nginx和Tomcat实现了负载均衡。Nginx将根据配置的负载均衡策略将客户端请求分发到多个Tomcat服务器上,以提高系统的性能和可用性。请注意,在实际生产环境中,还需要进行其他配置和优化,如健康检查、会话保持等,以满足具体的需求。
34 0
|
3月前
|
负载均衡 安全 前端开发
百度搜索:蓝易云【Nginx与Tomcat负载均衡-动静分离教程】
这些是将Nginx与Tomcat结合使用实现负载均衡和动静分离的基本步骤。根据您的需求和具体环境,可能还需要进行其他配置和调整。请确保在进行任何与网络连接和安全相关的操作之前,详细了解您的网络环境和安全需求,并采取适当的安全措施。
48 1
|
4月前
|
负载均衡 安全 应用服务中间件
Nginx + Tomcat+HTTP/HTTPS实现负载均衡实例
Nginx + Tomcat+HTTP/HTTPS实现负载均衡实例
179 0
|
4月前
|
负载均衡 算法 应用服务中间件
Nginx+Tomcat实现反向代理与负载均衡入门
Nginx+Tomcat实现反向代理与负载均衡入门
183 0
|
4月前
|
负载均衡 应用服务中间件 nginx
Docker配置1台Nginx+3台Tomcat做负载均衡
Docker配置1台Nginx+3台Tomcat做负载均衡
211 0
|
4月前
|
负载均衡 网络协议 应用服务中间件
当当网266买来的1399页Nginx笔记,原来我入门都不算
Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。
当当网266买来的1399页Nginx笔记,原来我入门都不算
|
4月前
|
XML Java 应用服务中间件
# 如何 在同一个 服务器上同时部署 nginx 以及 tomcat
# 如何 在同一个 服务器上同时部署 nginx 以及 tomcat
75 0