前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >为什么不应该重写 service 方法?

为什么不应该重写 service 方法?

作者头像
Devops海洋的渔夫
发布于 2022-01-17 00:25:59
发布于 2022-01-17 00:25:59
23800
代码可运行
举报
文章被收录于专栏:Devops专栏Devops专栏
运行总次数:0
代码可运行

从前,有一个程序猿,他语重心长地对孙子说:“孩子,要是你以后写servlet,最好不要重写service方法啊”

孙子大为不解,程序猿又说:“听爷爷的,准没错,爷爷的爷爷就是这么说的……”

——为什么不应该重写service方法呢?

如果你也曾思考过这个问题,但暂时无解,这篇文章或许可以给你一点启发。

先来看一个具体的例子:

当时我正在osc看红薯的一篇大作,只见我右手F12熟练的打开了chrome的开发者工具,左手迅猛的按了几下F5,然后看到了这个结果。

聪明的你一定已经发现,除了第一个名为12_77118的请求返回状态为200,其他的都为304,那么200和304有什么区别呢?这个稍后解释。

一切从代码里面来,我们先抛开理论,看一个具体的code:

我编写了一个index.html,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<html>
<body>
<h3>I'm a test page . </h3>
<h3>I'm a test page . </h3>
<h3>I'm a test page . </h3>
<h3>I'm a test page . </h3>
<h3>I'm a test page . </h3>
<h3>I'm a test page . </h3>
<h3>I'm a test page . </h3>
</body>
</html>

我们来访问这个页面看看。

Image(2)

这是我第一次访问这个页面(表示本地并没有对这个文件的缓存):

我们来看看http请求和响应的消息头:

《图:一》

为了作为对比,我们再F5刷新一次:

《图:二》

这次请求的头信息中多了一条If-Modified-Since,而且返回的响应中,状态变为了304,这是怎么回事?还记得红薯那篇文章页中的304么,你会发现,304多出现在对于静态资源的请求上面。

原来对于静态资源来说:

  1. 当浏览器第一次发起请求时(请求头中没有If-Modified-Since),server会在响应中告诉浏览器这个资源最后修改的时间(响应头中的Last-Modified)。(见图一)
  2. 浏览器也很聪明,当你再次(点击链接,或者F5,或者回车,但是不能是ctrl+F5)请求这个资源时,浏览器会询问server这个资源自上次告诉我的最后修改时间以来有没有被修改(请求头中If-Modified-Since)。(见图二)
  3. 如果资源没有被修改,server返回304状态码,并不会再次将资源发送给浏览器,浏览器则很知趣的使用本地的缓存文件。(见图二)

所以所有的静态资源如果没有发生变化,通常是不会传递多次的,不管什么浏览器或者server都应该遵守这种询问的约定。看起来很爽啊,很智能是不是?这种约定的机制就是 http缓存协商——这是约定优于配置的又一体现。

有了缓存协商的知识,理解为什么我们不应该重写service就很容易了。还是从代码出发,这次我们看一个复杂一点的例子:

在这个例子中,我们请求一个控制器(MeServlet),然后转向一个视图(index.html),为了简单起见,web.xml中将只有这个servlet的配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<web-app>
 <servlet>
 <servlet-name>me</servlet-name>
 <servlet-class>com.me.web.MeServlet</servlet-class>
 </servlet>
 <servlet-mapping>
 <servlet-name>me</servlet-name>
 <url-pattern>/test</url-pattern>
 </servlet-mapping>
</web-app>

然后是MeServlet:
public class MeServlet extends HttpServlet {
 @Override
 protected void service(HttpServletRequest req, HttpServletResponse res)
 throws ServletException, IOException {
 /**
         * 1. 处理具体的业务:
         * -- 处理请求参数
         * -- 检查缓存
         * -- 处理具体数据
         * -- 更新缓存
         */
        doBizLogic(req, res);
 /**
         * 2. 根据处理的结果转向具体的视图:
         * -- 这里假设就是 index.html
         */
        getServletContext()
                .getRequestDispatcher("/index.html").include(req, res);
    }
 public void doBizLogic(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("do biz.");
    }
}

可以看到,每次F5刷新返回的状态码都是200,让我们看看具体的请求和响应头:

我们发现无论我们如何刷新页面,每次响应状态都是200,index.html的内容每次都被完整的发送给浏览器,这看起来很笨,为什么不像静态资源一样进行缓存协商呢?原因是缓存协商是基于http请求和响应头中的Modified信息的,如果没有这个信息,是无法进行缓存协商的。而对于动态内容而言,server无法帮我们决定内容是不是有改变,也无法替我们决定动态内容的最后修改时间。

所以它不会帮我们在响应中加上Last-Modified,我们必须自己来做这件事,我们小小地修改一下MeServlet:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MeServlet extends HttpServlet {
 @Override
 protected long getLastModified(HttpServletRequest req) {
 /**
         * 这里你要自己决定动态内容的最后修改时间,例如你可以返回
         * -- 数据缓存最后更新的时间
         * -- 简单起见,我们假设最后的修改时间是 1000
         */
 return 1000;
    }

 @Override
 protected void service(HttpServletRequest req, HttpServletResponse res)
 throws ServletException, IOException {
 /**
         * 1. 处理具体的业务:
         * -- 处理请求参数
         * -- 检查缓存
         * -- 处理具体数据
         * -- 更新缓存
         */
        doBizLogic(req, res);
 /**
         * 2. 根据处理的结果转向具体的视图:
         * -- 这里假设就是 index.html
         */
        getServletContext()
                .getRequestDispatcher("/index.html").include(req, res);
    }

 public void doBizLogic(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("do biz.");
    }
}

你会看到getLastModified这个方法是重写的,说明HttpServlet中已经有了这个方法,我们使用这个方法来告诉server在这个动态资源中,最后内容变化的时间是多少。最理想的情况是server会自己回调这个方法,那就太省心啦。

我们先访问的看看:发现依然每次都是200,server没有告诉浏览器最后的修改时间,缓存协商机制无法工作。

先别沮丧,忘了我们要解释什么问题吗——为什么不要重写service方法。也许你已经猜到了,如果你看看service方法的实现,现在你已经明白了,service方法自己实现了缓存协商的机制,如果我们重写它,反而将这中良好的机制给去掉了。

我们再修改一下,这次我们重写doGet,在doGet中完成完全相同的逻辑:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MeServlet extends HttpServlet {

 @Override
 protected long getLastModified(HttpServletRequest req) {
 /**
         * 这里你要自己决定动态内容的最后修改时间,例如你可以返回
         * -- 数据缓存最后更新的时间
         * -- 简单起见,我们假设最后的修改时间是 1000
         */
 return 1000;
    }

 @Override
 protected void doGet(HttpServletRequest req, HttpServletResponse res)
 throws ServletException, IOException {
 /**
         * 1. 处理具体的业务:
         * -- 处理请求参数
         * -- 检查缓存
         * -- 处理具体数据
         * -- 更新缓存
         */
        doBizLogic(req, res);
 /**
         * 2. 根据处理的结果转向具体的视图:
         * -- 这里假设就是 index.html
         */
        getServletContext()
                .getRequestDispatcher("/index.html").include(req, res);
    }
 public void doBizLogic(HttpServletRequest request, HttpServletResponse response) {
        System.out.println("do biz.");
    }
}

这次在访问,

终于出现了久违的Last-Modified,再次回车请求页面,哈哈变成304了。

现在你也许已经清楚了,为什么不应该重写service方法,似乎是为了保留HttpServlet默认实现的缓存协商的机制;其实还有另外一个原因:就是禁用你没有在servlet中重写的方法,例如post、head等,这样就从一定程度上提高了安全性。

理论到此为止,现在让我们来看看缓存协商机制有什么实际的好处:

还是红薯的那边文章,我们现在全加载(ctrl+F5)一次看看,

我们看到总共发起了45个请求,请求的数据量为198.93KB,然后F5刷新一次:

这次只有36个请求,数据量只有23.62KB

我们看到这篇文章被9960个id访问, 而每一个id实际上可能访问这个页面多次(像我这样,实际的数据可能得问问红薯),然后我们看到很多304静态资源都是整站通用的:

如果你是osc的常客,并且不经常更换浏览器,不经常清理缓存,甚至其他人的头像都可以是通用的,为了简单起见,我们这里考虑每个id都只访问这个页面一次,并且假设所有的资源都已经缓存在用户本地,得出:

(198.93-23.62)×9960 = 1746086.6KB = 1705.1637M = 1.665G。

很惊人吧,这只是一个页面,别忘了,我们还假设所有的用户都只访问一次,你想想osc上面有多少篇博文,加起来……

流量是什么,是银子啊。

幸运的是,这些省银子的事情浏览器和server都已经帮我们做好了,那我们就不需要关心这个了吗??我们看到12_77118这个请求所占用的资源也不少,如果文章再长点,再长点的话……还会更大。

如果红薯愿意,也可以让这个请求实现缓存协商,可以进一步减少流量。

当然这里的计算并不是完全的精确,实际的情况复杂很多,但是这个计算的量级应该是对的,是值得参考的。

流量涉及的另一个问题就是带宽,以更小的贷款提供更高的并发是每个站长应该追求的。不过考虑到osc以新闻为主,一次性消费,所以……不过那时题外话了。

好了,如果你有耐心看到这里,我想你也许会对service有了新的理解,为什么我们不应该重写这个方法。

万事有例外,如果你需要实现一个前端控制器的话,就是另外一回事了,这留给大家自己思考。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-03-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 海洋的渔夫 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
nginx--❤️图解及代码实现正向代理、反向代理、负载均衡❤️
在实现nginx正向代理之前,先说明一下,现在的网站基本上都是https,因此要实现nginx正向代理转发请求,除了要配置转发http80端口的请求,还有配置https443端口的请求~
用户4396583
2024/07/25
1.3K0
Nginx到底能做什么?这篇文章带你全面了解
链接:https://www.jianshu.com/p/8bf73d1a758c
用户6543014
2019/10/25
3740
Nginx到底能做什么?这篇文章带你全面了解
Nginx(三)------nginx 反向代理
  Nginx 服务器的反向代理服务是其最常用的重要功能,由反向代理服务也可以衍生出很多与此相关的 Nginx 服务器重要功能,比如后面会介绍的负载均衡。本篇博客我们会先介绍 Nginx 的反向代理,当然在了解反向代理之前,我们需要先知道什么是代理以及什么是正向代理。
IT可乐
2018/09/28
2.6K0
Nginx(三)------nginx 反向代理
Nginx反向代理的配置
最近搞个服务器,然后部署了个人网站,以及一些服务供自己玩耍,其中Nginx最让我很难受,配置总是要现用现百度。所以,我来了,咳咳,我带来了一些​常用的配置。​需要有 Linux 和 Nginx 环境。
Java技术债务
2022/08/09
1.4K0
Nginx配置——反向代理
其他话不多说,下面来学习一下Nginx的反向代理。如果没有Linux和Nginx环境,可以参考前两篇文章。
全栈程序员站长
2022/09/14
8.4K0
Nginx配置——反向代理
全面了解 Nginx 到底能做什么
本文只针对Nginx在不加载第三方模块的情况能处理哪些事情,由于第三方模块太多所以也介绍不完,当然本文本身也可能介绍的不完整,毕竟只是我个人使用过和了解到过得。所以还请见谅,同时欢迎留言交流
芋道源码
2018/09/30
3360
nginx使用学习之正向代理、反向代理、负载均衡(配置实例详解)
Nginx 不仅可以做反向代理,实现负载均衡。还能用作正向代理来进行上网等功能。正向代理:如果把局域网外的 Internet 想象成一个巨大的资源库,则局域网中的客户端要访 问 Internet,则需要通过代理服务器来访问,这种代理服务就称为正向代理。
IT大咖说
2019/12/17
5K0
nginx使用学习之正向代理、反向代理、负载均衡(配置实例详解)
Nginx 反向代理学习及实例笔记
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011415782/article/details/78599794
泥豆芽儿 MT
2018/09/11
3K0
Nginx 反向代理学习及实例笔记
Nginx专题(1):Nginx之反向代理及配置
摘要:本文从Nginx的概念出发,分别从反向代理的概念、优势、配置代码3个方面介绍了Nginx的特性之一反向代理。
宜信技术学院
2019/11/06
2K0
nginx反向代理与负载均衡
如果我们无法访问到谷歌网站的时候,就可以设置一个代理,然后我们去访问代理服务器,代理会向谷歌发送请求,然后再将数据返回给我们。这样的代理就是正向代理。
兰舟千帆
2022/08/05
6470
nginx反向代理与负载均衡
简单说说什么是正向代理和反向代理,以及他们的区别|nginx反向代理配置教程
一般的访问流程是客户端直接向目标服务器发送请求并获取内容,比如大家通过浏览器,输入对应的网址,baidu.com、qq.com、taobao.com、zygxsq.cn/daohang 等,就可以直接进入对方的网站。
小小鱼儿小小林
2024/05/25
2980
PHP使用Nginx实现反向代理
一、代理服务器 1、什么是代理服务器 代理服务器,客户机在发送请求时,不会直接发送给目的主机,而是先发送给代理服务器,代理服务接受客户机请求之后,再向主机发出,并接收目的主机返回的数据,存放在代理服务器的硬盘中,再发送给客户机。
用户2323866
2021/07/07
1.8K0
Nginx正向代理与反向代理 负载均衡
Nginx #1 什么是正向代理 正向代理类似一个跳板机,代理访问外部资源 举个例子: 我是一个用户,我访问不了某网站(用户知道该网站的服务器地址),但是我能访问一个代理服务器,这个代理服务器
Autooooooo
2020/11/09
8830
Nginx正向代理与反向代理 负载均衡
Nginx实现虚拟主机、反向代理、负载均衡、高可用、web缓存
​ 虚拟主机是一种特殊的模拟硬件的软件技术,它可以将网络上的一台物理计算机映射成多个虚拟主机,每个虚拟主机可以独立对外提供www服务,这样就可以实现一台物理主机对外提供多个web服务了。并且每个虚拟主机之间是独立的,互不影响的。
青山师
2023/05/05
7780
Nginx实现虚拟主机、反向代理、负载均衡、高可用、web缓存
【Nginx】静态资源部署、反向代理、负载均衡
注:http块中可配置多个Service块,每个Service块可配置多个location块。
.29.
2023/10/17
1.7K0
【Nginx】静态资源部署、反向代理、负载均衡
05 . Nginx的反向代理与负载均衡
Nginx负载均衡 客户端的访问都被代理到后端的一台服务器上,最终会出现性能瓶颈,从而导致效率降低,前端用户的访问速度急速下降,要解决这个问题就需要添加多台httpd,同时承受大量并发连接,每台服务器接收的并发连接降低了,效率就高了,nginx作为代理,在收到客户端的请求时,会分发到不同的后端服务器,但是nginx收到请求之后会分配到那个服务器之上,有什么规律和需求,就需要用调度算法来分配. upstream语法 # upstream的定义必须在server{..}外定义 upstream <组名
iginkgo18
2020/09/27
8910
【前端词典】如何向老板解释反向代理
由于我家老板看过之后,对这篇文章的评价是:写的不错,语句是通顺的,排版是可以的,但反向代理是什么还是不清楚?所以我就想尝试着向非 IT 工作者解释“正向代理”和“反向代理”。
小生方勤
2019/06/02
5990
如何使用Nginx 部署项目?什么是反向代理?
初识Nginx + Linux 中安装Nginx_一切总会归于平淡的博客-CSDN博客_nginx编译安装
叫我阿杰好了
2022/11/07
6050
如何使用Nginx 部署项目?什么是反向代理?
nginx反向代理与负载均衡
     正向代理,就是我们常用的代理,类似于一个跳板,通俗的来说就是,我访问不了某网站,例如我想访问pornhub,但是由于一些不可描述的原因,我们是无法直接访问的,但是我们有一台服务器,这台服务器可以访问到该站,而我们恰好也能访问该服务器,所以我们就先连上这台服务器,告诉他,我想访问pornhub,然后这台服务器便去访问,最后将结果返回给我。
dogfei
2020/07/31
6950
Nginx 主要应用场景
本文只针对Nginx在不加载第三方模块的情况能处理哪些事情,由于第三方模块太多所以也介绍不完,当然本文本身也可能介绍的不完整,毕竟只是我个人使用过和了解到过得。所以还请见谅,同时欢迎留言交流
用户4283147
2022/10/27
4610
推荐阅读
相关推荐
nginx--❤️图解及代码实现正向代理、反向代理、负载均衡❤️
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档