12月4
最近公司有个项目需要对本地hosts文件进行修改,添加一条本地域名解析记录,如果让客户去操作,很容易破坏掉原先的hosts文件,最好是能封装成一个批处理脚本,用户只需要以管理员权限运行,然后根据菜单选择需要执行的功能即可。

1 批处理脚本
:: 关闭命令的回显
@echo off
:: 设置hosts文件路径
set HOSTS=C:\Windows\System32\drivers\etc\hosts
:: 判断当前路径是否存在 hosts.init 文件, 不存在则备份
if not exist %HOSTS%.init (
    copy /y %HOSTS% %HOSTS%.init && echo 系统hosts文件备份完成!
)

:: %date%=2022年12月04日
:: %time%= 14:45:38:96
:: 年 yyyy = %date:~0,4% 表示指针从左到右偏移 0 位, 然后提取 4 字符
:: 月 mm   = %date:~5,2% 表示指针从左到右偏移 5 位, 然后提取 2 字符
:: 日 dd   = %date:~8,2% 表示指针从左到右偏移 8 位, 然后提取 2 字符
set yyyymmdd=%date:~0,4%-%date:~5,2%-%date:~8,2%
:: 时 hh   = %time:~0,2% 表示指针从左到右偏移 0 位, 然后提取 2 字符
:: 分 mm   = %time:~3,2% 表示指针从左到右偏移 3 位, 然后提取 2 字符
:: 秒 ss   = %time:~6,2% 表示指针从左到右偏移 6 位, 然后提取 2 字符
set hh=%time:~0,2%
set mm=%time:~3,2%
set ss=%time:~6,2%
:: 当 hh 小于等于 9 时, 在前面补 0
if %hh% leq 9 (set hh=0%hh:~1,1%)

GOTO MENU
:MENU
ECHO.
ECHO.############################## 修改系统hosts解析文件 ##############################
ECHO.#
ECHO.# 1.添加域名解析
ECHO.#
ECHO.# 2.编辑hosts文件
ECHO.#
ECHO.# 3.备份hosts文件
ECHO.#
ECHO.# 4.还原hosts文件
ECHO.#
ECHO.# 5.刷新dns缓存并退出
ECHO.#
ECHO.############################### %yyyymmdd% %hh%:%mm%:%ss% ###############################
set /p="请输入您要操作的序号:"<nul
@set /p  sel=
if "%sel%"=="1"  goto add_ipdomain
if "%sel%"=="2"  goto edit_hosts
if "%sel%"=="3"  goto backup_hosts
if "%sel%"=="4"  goto reset_hosts
if "%sel%"=="5"  goto refresh_dns
PAUSE
:add_ipdomain
set /p="请输入IP地址和域名(中间以空格分隔):"<nul
@set /p  ip_domain=
type %HOSTS% |findstr /v "^#"| findstr /i "%ip_domain%\>"
if %errorlevel% == 0 (
  echo %ip_domain% 已存在,请重新添加!&& goto MENU
) else (
  echo.%ip_domain% >> %HOSTS% && echo %ip_domain% 添加成功,请按任意键返回菜单!&& pause >nul
)
goto MENU
:edit_hosts
title 编辑hosts本地域名解析文件
notepad %HOSTS%
echo 按任意键返回菜单!
pause >nul
goto MENU
:backup_hosts
title 备份hosts文件
set yyyymmdd=%date:~0,4%%date:~5,2%%date:~8,2%
copy /y %HOSTS% %HOSTS%.%yyyymmdd%%hh%%mm%
echo ("%HOSTS%.%yyyymmdd%%hh%%mm%") 备份完毕,按任意键返回菜单!
pause >nul
goto MENU
pause
:reset_hosts
title 还原hosts文件
copy /y %HOSTS%.init %HOSTS%
echo hosts文件已还原至初始状态,按任意键返回菜单!
pause >nul
goto MENU
:refresh_dns
title 刷新本地dns缓存并退出
echo 查看修改后的hosts文件内容,5秒后退出!
type "%HOSTS%" |findstr /v "^#"|findstr "[0-9]"
ping -n 5 127.0.0.1>nul
ipconfig /flushdns
echo 刷新本地缓存成功,即将退出!
ping -n 3 127.0.0.1>nul
echo.
exit

2 使用方法
2.1 功能菜单
第一次以管理员权限运行脚本会备份一份hosts.init文件,作为hosts初始文件,方便以后还原。



2.2 添加域名解析
添加域名解析这里采用的是手动输入 IP 地址 + 域名,并且会检索hosts文件里是否已经存在对应的 IP 地址和域名,如果存在,则提示用户重新添加。



2.3 编辑 hosts 文件
这里其实就是调用记事本来打开hosts文件。



2.4 备份 hosts文件
这里的备份文件是以hosts.日期时间的文件名格式来备份的。



2.5 还原 hosts文件
还原其实就是将 hosts.init 去掉后缀名,替换掉原先的 hosts 文件。



2.6 刷新 dns 缓存并退出
在刷新 dns 前,会先将 hosts 文件中的内容打印一下,然后执行刷新 dns 命令后退出脚本。

12月1

浅谈HTTP中Get与Post的区别

15:57Web开发  From: 本站原创

Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE。URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的4个操作。到这里,大家应该有个大概的了解了,GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。

  1.根据HTTP规范,GET用于信息获取,而且应该是安全的和幂等的

  (1).所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改,增加数据,不会影响资源的状态。

  * 注意:这里安全的含义仅仅是指是非修改信息。

  (2).幂等的意味着对同一URL的多个请求应该返回同样的结果。这里我再解释一下幂等这个概念:

幂等(idempotent、idempotence)是一个数学或计算机学概念,常见于抽象代数中。
  幂等有一下几种定义:
  对于单目运算,如果一个运算对于在范围内的所有的一个数多次进行该运算所得的结果和进行一次该运算所得的结果是一样的,那么我们就称该运算是幂等的。比如绝对值运算就是一个例子,在实数集中,有abs(a)
=abs(abs(a))
  对于双目运算,则要求当参与运算的两个值是等值的情况下,如果满足运算结果与参与运算的两个值相等,则称该运算幂等,如求两个数的最大值的函数,有在在实数集中幂等,即max(x,x) 
= x

看完上述解释后,应该可以理解GET幂等的含义了。

  但在实际应用中,以上2条规定并没有这么严格。引用别人文章的例子:比如,新闻站点的头版不断更新。虽然第二次请求会返回不同的一批新闻,该操作仍然被认为是安全的和幂等的,因为它总是返回当前的新闻。从根本上说,如果目标是当用户打开一个链接时,他可以确信从自身的角度来看没有改变资源即可。

  2.根据HTTP规范,POST表示可能修改变服务器上的资源的请求。继续引用上面的例子:还是新闻以网站为例,读者对新闻发表自己的评论应该通过POST实现,因为在评论提交后站点的资源已经不同了,或者说资源被修改了。

 

  上面大概说了一下HTTP规范中GET和POST的一些原理性的问题。但在实际的做的时候,很多人却没有按照HTTP规范去做,导致这个问题的原因有很多,比如说:

  1.很多人贪方便,更新资源时用了GET,因为用POST必须要到FORM(表单),这样会麻烦一点。

  2.对资源的增,删,改,查操作,其实都可以通过GET/POST完成,不需要用到PUT和DELETE。

  3.另外一个是,早期的Web MVC框架设计者们并没有有意识地将URL当作抽象的资源来看待和设计,所以导致一个比较严重的问题是传统的Web MVC框架基本上都只支持GET和POST两种HTTP方法,而不支持PUT和DELETE方法。

   * 简单解释一下MVC:MVC本来是存在于Desktop程序中的,M是指数据模型,V是指用户界面,C则是控制器。使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式。

  以上3点典型地描述了老一套的风格(没有严格遵守HTTP规范),随着架构的发展,现在出现REST(Representational State Transfer),一套支持HTTP规范的新风格,这里不多说了,可以参考《RESTful Web Services》。

 

  说完原理性的问题,我们再从表面现像上面看看GET和POST的区别

  1.GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以?分割URL和传输数据,参数之间以&相连,如:login.action?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5%BD。如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密,得出如:%E4%BD%A0%E5%A5%BD,其中%XX中的XX为该符号以16进制表示的ASCII。

  POST把提交的数据则放置在是HTTP包的包体中。

  2."GET方式提交的数据最多只能是1024字节,理论上POST没有限制,可传较大量的数据,IIS4中最大为80KB,IIS5中为100KB"??!

  以上这句是我从其他文章转过来的,其实这样说是错误的,不准确的:

  (1).首先是"GET方式提交的数据最多只能是1024字节",因为GET是通过URL提交数据,那么GET可提交的数据量就跟URL的长度有直接关系了。而实际上,URL不存在参数上限的问题HTTP协议规范没有对URL长度进行限制。这个限制是特定的浏览器及服务器对它的限制。IE对URL长度的限制是2083字节(2K+35)。对于其他浏览器,如Netscape、FireFox等,理论上没有长度限制,其限制取决于操作系统的支持。

  注意这是限制是整个URL长度,而不仅仅是你的参数值数据长度。[见参考资料5]

  (2).理论上讲,POST是没有大小限制的HTTP协议规范也没有进行大小限制,说“POST数据量存在80K/100K的大小限制”是不准确的,POST数据是没有限制的,起限制作用的是服务器的处理程序的处理能力。

  对于ASP程序,Request对象处理每个表单域时存在100K的数据长度限制。但如果使用Request.BinaryRead则没有这个限制。

  由这个延伸出去,对于IIS 6.0,微软出于安全考虑,加大了限制。我们还需要注意:

     1).IIS 6.0默认ASP POST数据量最大为200KB,每个表单域限制是100KB。
     2).IIS 6.0默认上传文件的最大大小是4MB。
     3).IIS 6.0默认最大请求头是16KB。
  IIS 6.0之前没有这些限制。[见参考资料5]

  所以上面的80K,100K可能只是默认值而已(注:关于IIS4和IIS5的参数,我还没有确认),但肯定是可以自己设置的。由于每个版本的IIS对这些参数的默认值都不一样,具体请参考相关的IIS配置文档。

  3.在ASP中,服务端获取GET请求参数用Request.QueryString,获取POST请求参数用Request.Form。在JSP中,用request.getParameter(\"XXXX\")来获取,虽然jsp中也有request.getQueryString()方法,但使用起来比较麻烦,比如:传一个test.jsp?name=hyddd&password=hyddd,用request.getQueryString()得到的是:name=hyddd&password=hyddd。在PHP中,可以用$_GET和$_POST分别获取GET和POST中的数据,而$_REQUEST则可以获取GET和POST两种请求中的数据。值得注意的是,JSP中使用request和PHP中使用$_REQUEST都会有隐患,这个下次再写个文章总结。

  4.POST的安全性要比GET的安全性高。注意:这里所说的安全性和上面GET提到的“安全”不是同个概念。上面“安全”的含义仅仅是不作数据修改,而这里安全的含义是真正的Security的含义,比如:通过GET提交数据,用户名和密码将明文出现在URL上,因为(1)登录页面有可能被浏览器缓存,(2)其他人查看浏览器的历史纪录,那么别人就可以拿到你的账号和密码了,除此之外,使用GET提交数据还可能会造成Cross-site request forgery攻击。

  总结一下,Get是向服务器发索取数据的一种请求,而Post是向服务器提交数据的一种请求,在FORM(表单)中,Method默认为"GET",实质上,GET和POST只是发送机制不同,并不是一个取一个发!

  纯属hyddd个人总结,如有错漏请指出。:>

 

参考资料

[1].http://hi.baidu.com/liuzd003/blog/item/7bfecbfa6ea94ed8b58f318c.html

[2].http://www.blogjava.net/onlykeke/archive/2006/08/23/65285.aspx

[3].http://baike.baidu.com/view/2067025.htm

[4].http://www.chxwei.com/article.asp?id=373

[5].http://blog.csdn.net/somat/archive/2004/10/29/158707.aspx

12月1

Http协议是什么?

15:54Web开发  From: 本站原创

一、什么是Http协议

  超文本传输协议的简称,用于定义客户端与web服务器通迅的格式。

  关于【标准的HTTP协议是无状态的】,请参见:https://www.heckjj.com/post/628/

二、Http的版本区别

  1.0:客户端与web服务器建立连接后,只能获得一个web资源

  1.1:允许客户端与web服务器建立连接后,在一个连接上获取多个web资源

三、Http协议格式

1.请求:客户端向服务器发出的请求

  请求行(常为GET/POST请求方式)

    GET: (默认的请求方式),在URL地址后附带的参数,但是有限制的,其数据容量通常不能超过1K。对应的查——用于查询资源信息

    POST: 可以在请求的实体内容中向服务器发送数据,Post方式的特点:传送的数据量无限制。对应改——用于更新资源信息

  GET与POST的区别请参见https://www.heckjj.com/post/630/
  请求头:头值
  请求体
  

HTTP请求中的常用消息头

  accept:浏览器通过这个头告诉服务器,它所支持的数据类型
  Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
  Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式
  Accept-Language:浏览器通过这个头告诉服务器,它的语言环境
  Host:浏览器通过这个头告诉服务器,想访问哪台主机
  If-Modified-Since: 浏览器通过这个头告诉服务器,缓存数据的时间
  Referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的  防盗链
  Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是何持链接

2.响应:服务端向客户端送回的数据

  响应行( 协议/版本 状态码/状态码的解析 ) (状态行,处理的结果)
  响应头(key/value格式):对数据的描述以及告知客户端如何处理送回的数据

HTTP响应中的常用响应头(消息头)
  Location: 服务器通过这个头,来告诉浏览器跳到哪里
  Server:服务器通过这个头,告诉浏览器服务器的型号
  Content-Encoding:服务器通过这个头,告诉浏览器,数据的压缩格式
  Content-Length: 服务器通过这个头,告诉浏览器回送数据的长度
  Content-Language: 服务器通过这个头,告诉浏览器语言环境
  Content-Type:服务器通过这个头,告诉浏览器回送数据的类型
  Refresh:服务器通过这个头,告诉浏览器定时刷新

  Content-Disposition: 服务器通过这个头,告诉浏览器以下载方式打数据
  Transfer-Encoding:服务器通过这个头,告诉浏览器数据是以分块方式回送的
  Expires: -1  控制浏览器不要缓存
  Cache-Control: no-cache  
  Pragma: no-cache
  空行
  响应体

常见状态码:

  HTTP/1.1 200 OK HTTP版本1.1 状态码200 解码表示ok(2开头的都是ok
       404(客户端的错误,资源找不到)(4打头客户端错误
       405 不支持该请求(没有重写相关的方法doGet() doPost())
       500 请求的资源找到了,但服务器内部出现了错误(5开头服务器错误
       503 服务器暂时不可用
       302 重定向。带一个响应头Location(3开头都是中转相关
       304 未修改 Not Modified。告诉浏览器最后修改时间是正确最新的,节省传输成本
         (Last-Modifired (Since))当然针对的是HTML静态页面

12月1
引言:
最近在好好了解http,发现对介绍http的第一句话【http协议是无状态的,无连接的】就无法理解了:无状态的【状态】到底指的是什么?!
找了很多资料不仅没有发现有一针见血正面回答这个问题的,而且有些解释还充斥了各种错误,看着看着就觉得心里憋着一股浊气吐不出来
于是在看了很多资料之后,我一口吐出浊气,大声正面提出这个问题:http协议无状态中的【状态】到底指的是什么?!
然后开始不断探索解决这个问题。。。
最终很高兴的是我找到了让人满意的答案,先卖个关子,各位如果着急可以直接拉到最下查看

正文:http协议无状态中的【状态】到底指的是什么呢?

先来看这句话的另外两个概念:(标准的http协议是无状态的,无连接的)
标准的http协议指的是不包括cookies, session,application的http协议,他们都不属于标准协议,虽然各种网络应用提供商,实现语言、web容器等,都默认支持它
无连接指的是什么
每一个访问都是无连接,服务器挨个处理访问队列里的访问,处理完一个就关闭连接,这事儿就完了,然后处理下一个新的
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接

对于【无状态】,我看到很多隔着一层磨砂玻璃一样的模糊说法(官方或者教程里的说法),看着非常难受(但其实算是对的)(后来我发现我为什么觉得它看着难受了,因为他们引入了很多新的,而且明显是一个可能用在很多地方的广义名词,这些词最大的作用就是,混淆概念,下面我标注了)
协议对于事务处理没有记忆能力【事物处理】【记忆能力】
对同一个url请求没有上下文关系【上下文关系】
每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况【无直接联系】【受直接影响】
服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器【状态】

我必须得到确切而具体的解释!

这几点给了我下一步思考的方向:
【服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器 】这里的客户端的状态是不是确切地指服务器没有保存客户的信息呢?但显然不是啊
【HTTP无状态的特性严重阻碍了这些应用程序的实现,毕竟交互是需要承前启后的,简单的购物车程序也要知道用户到底在之前选择了什么商品】我对此质疑为什么无状态就不能实现购物车呢?服务器就不能存储东西了么?
【 每次的请求都是独立的,<它的执行情况和结果>与<前面的请求>和<之后的请求>是无直接关系的】我觉得这个说法比较靠谱,但是所谓的不同请求间的没有关系,是指的请求内容没有关系,还是只是指请求本身没有关系?
请求内容没有关系只可能是服务器上不存有用户数据才可能啊,但是显然是存有的啊
请求本身没有关系,这又有什么意义呢,每一次的请求有什么价值?

根据这个方向我做了一个模拟访问实验:假如没有cookie没有session,只有http的时候,那当一个注册用户访问这个购物网站的时候,会发生这些事情:
前提情况:
服务器肯定为每个注册用户建立了数据表,记录用户的数据
http是无连接的
第一步需要登录
用户通过http把用户的用户名和密码发送给服务器,服务器把他们跟自己存有的用户资料对比,如果一致,则返回信息登录成功
然后用户点击某一商品页
这个动作相当于输入一个商品页的网址
假如商品页比较机密不对外公开,需要是用户才能访问
而虽然http能传送用户名和密码,而且刚才也输入了,还验证成功了,但是因为服务器既不会记得你登录的状态,你的客户端也不会存储你刚才输入的用户名和密码
所以因为这一次访问因为无法确定你的身份,只能访问失败
这时候如果要解决这个问题,而且没有cookie没有session,那就只能你在访问网址的同时继续带上你的用户名和密码(继续输入咯)其实就像我现在的APP一样
假设上一步的问题解决了,就是每次访问的时候都会手动输入用户名和密码,然后现在的情况是:你已经选了几件商品在你的购物车中,你想再添加一件商品,于是你点击某个商品旁边的加号
这个动作也相当于输入一个网址,网址的内容是发送一个请求,往你的购物车中加入这个商品
系统首先用你传来的用户名和密码验证你的身份,然后访问你的数据库,在其中的购物车属性下加一条数据,就是这个商品的数据
操作结束后,返回操作成功,并结束访问
OK,实验结束,看似没有cookie没有session也能凑合解决问题,其实两个操作都有很大的问题
你每访问一次需要权限的内容都需要在客户端输入用户名和密码,这一项的繁琐就不必赘述了
你的每一次操作都要与系统底层的数据库进行交互
多次少量的访问存在非常大的性能浪费。非常容易就能想到肯定是一次大量的操作更加有效率,于是就想到了缓存区
你的非重要琐碎数据也被写进数据库中,跟你的主要数据放在一起
一次次添加和删除购物车其实只是跟你这次浏览,或者叫这次会话有关,是临时的数据,跟用户的主要信息无关,它们没什么价值,纯粹的冗余数据(不排除现在有的公司觉得这种数据也有非常大的价值可以让它们巧妙的利用),用什么存放这些临时的数据,我们也很容易想到缓存区

经过这个模拟访问实验,结合前面的思考方向,我们知道了三点:
服务器上肯定存有用户的数据,你提交的增删改查它也能够处理,所以这句话中【服务器中没有保存客户端的状态】的状态并不是指用户的数据,我们的猜测不对
我们的质疑对了,无状态能实现购物车,可以通过服务器上存有的用户数据来实现
但是,使用上面这种方式实现购物车,存在三个比较大的问题。由此,我们不禁会想,这三个问题的解决是不是跟我们不确切了解的【状态】一词有关?于是,接下来我们来通过解决这三个问题来把【状态】的意义探寻下去

由上所述,我们可以在http的基础上增加一些机制来解决上面出现的三个问题
在用户端增加一个记录本是非常有必要的,正好官方加入的cookie机制跟这个一样,它的用处也确实是上面讨论的那样,一般就是用来标识访问者的身份
在服务器增加一个缓存区能同时解决后两个问题
有了这个缓存区作为一个数据缓冲,就不用一次次地访问数据库,浪费大量计算机资源,而是在最后统一归入数据库
有了这个缓存区,你就不用把临时的数据放到数据库中了,只需要在你们交流告一段落之后,再把数据整理,把有用的数据归入数据库
这里就自然引申出了一个重要的概念:会话,它作为一个缓冲存储区被从数据库中分离出来,理由并不生硬,它有其独特的重要且不可替代的作用。这个东西恰好跟官方加入的session机制一样
另外说一个非常具有迷惑性的容易让人对session的主要作用产生偏离的理解:认为session存在的价值就是给访问者分配一个sessionID代替用户名和密码,
为什么非常具有迷惑性,因为session确实做了这件事,而且也起到了很大的作用,所以它是对的,但是只对一半,而且没有涉及问题的本质,这种情况是最危险的(看似很有说服力,把你说服了,所以你很难有动力继续找下去,但是真实情况跟它有偏差,但是偏差不大,所以又很难把你说服回来,只有隐隐的不对劲,这个时候你离真实最近,也离真实最远)
那就顺便说说它为什么是对的,也就是用session做的另一件有用的事:
给每个session一个ID,一方面用来方便自己查询,另一方面把这个ID给用户,用户下一次访问的时候就可以不用用户名和密码,而是直接使用这个ID来表明自己的身份
首先,这个ID安全吗?这个ID比直接传用户名和密码安全吗?
你很容易会想到,本来用户名和密码的组合还特地设置地比较复杂,你这换一组数字就代替了,是不是太不安全了?
我们知道http协议本身是完全不加密的,如果使用用户名和密码,第一次访问是放在http头中,后边自动保存了密码就会放在cookie中,这些都完全没有加密,它的安全性基本为0,就是裸奔了,只要被窃取,那就丢失了
所以,就这个意义来讲,sessionID的安全性跟使用用户名和密码没什么区别
但是其实,虽然http本身不能加密,但是有些软件什么的,能在应用层面手动给你加密,比如QQ就会使用户名密码加临时验证码联合哈希,sessionID加一个时间戳简单加密也是非常常用的方法
而且因为sessionID本身有有效期,即使丢了,也可能很快失效,造成的损失可能没那么大,而用户名跟密码丢了,那就大了
所以总结就是:
不严格加密的sessionID和用户名和密码一样,都不太安全
但是相比较来说,sessionID要安全一些
而使用https是完全安全的
然后,使用sessionID有哪些好处
方便直接根据ID查询用户对应的session
加密的时候计算量小
安全性不会降低,甚至还更高一些

OK,通过独立地解决纯http机制会产生的问题,我们探讨了cookie和session机制的本质。而且想到:【使用http协议,服务器中不会保存客户端的状态】所产生的问题通过增加cookie和session机制解决了,是不是就意味着这个【状态】跟cookie和session的关系非常紧密?所以这个无状态指的是【没有对 本次会话 设置一个缓存区,记录这次会话的状态,缓存区包括服务器端和用户端】但好像还是没有点破关键(主要是觉得跟前面那些官方对状态的说法不太吻合,甚至没有对应关系)

忽然我想到一个问题:一个有状态的http是什么样的?
很难直接想象有状态的http是什么样,因为http这种机制是天然无状态的
那就类比一下吧,另一个天然有状态的机制叫TCP
如果有状态的意思是它的每次请求是有联系的,那么有状态的TCP的样子是:假如一份数据分了三份TCP包发送,那这个包上面会标明这是第几个包,会标明这个包跟那几个包是有联系的,有什么联系
但好像这个有状态的TCP跟我们想要的有状态的HTTP没有关系,因为即使每次http请求之间互相有联系,它也不能解决上面提到的http无状态的问题
诶,等等,好像能类比:
假如每个http连接都有一个签名,于是第一次登陆成功之后,服务器就知道了这个签名是允许登陆的,于是之后所有同样签名的http连接都能登陆,这里利用了同一个用户发出的http连接之间的同主人关系,这里解决了一个保持登录状态的问题
同样,来尝试利用这个【每次http请求之间互相有联系】来解决上面碰到的那个问题【每一次操作都要与系统底层的数据库进行交互】,但想了半天确实无法进行下去
不过我灵机一动,从另一个角度来想,好像解决了这个问题:
只有【每次http请求之间互相有联系】这个条件,无法解决【每一次操作都要与系统底层的数据库进行交互】
因为很明显,要解决【每一次操作都要与系统底层的数据库进行交互】就必须在服务器端开辟一块缓存区
不过如果你思考一下如何实现【每次http请求之间互相有联系】,你就会发现,它也需要在服务器端开辟一块缓存区
所以【在服务器端开辟一块缓存区】才是真正的条件,也就是说,它确实等价于【有状态】
而且我也找到了这个【在服务器端开辟一块缓存区】的条件跟前面那些官方对状态的说法对应的点,那就是:
通过在服务器端开辟一块缓存区,存储、记忆、共享一些临时数据,你就可以:
协议对于事务处理有记忆能力【事物处理】【记忆能力】
对同一个url请求有上下文关系【上下文关系】
每次的请求都是不独立的,它的执行情况和结果与前面的请求和之后的请求是直接关系的【不独立】【直接关系】
服务器中保存客户端的状态【状态】
所以,这个状态,加上前面说的客户端也有cookie,就是指,客户端和服务器在临时会话中产生的数据!而前面也说道了,使用缓存区保存临时会话中的数据是多么重要
所以状态不仅包括不同URL访问之间的关系,还有对其他URL访问的数据记录,还有一些其他的东西,所以更确切地说,状态应该是【实现了这些东西所凭借的后面的缓存空间】中的客户的临时数据
cookie和session应该是完全实现了有状态这个功能

一种常见的对状态的误解:
有人在解释HTTP的无状态时,把它跟有连接对立,说是两种方式,也就是如果想不无状态,就必须有连接,但其实不然
有连接和无连接以及之后的Keep-Alive都是指TCP连接
有状态和无状态可以指TCP也可以指HTTP
TCP一直有状态,HTTP一直无状态,但是应用为了有状态,就给HTTP加了cookie和session机制,让使用http的应用也能有状态,但http还是无状态
开始TCP是有连接,后来TCP无连接,再后来也就是现在TCP是Keep-Alive,有点像有连接。
12月1

Hutool工具类之Http工具

15:51Web开发  From: 本站原创
最简单最直接的上手可以参见参考文档:https://hutool.cn/docs/#/

受够了的HttpClient?那Hutool的HttpUtil值得一试!

一、HttpUtil 
快读使用:
发送get请求,包括有参与无参 
示例:
String url = "http://www.heckjj.com";
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("query", heck);
        // 无参GET请求
        String result = HttpUtil.get(url);
        // 带参GET请求
        String result2 = HttpUtil.get(url, paramMap);
      
发送post请求
这里直接引用文档的示例:

HashMap<String, Object> paramMap = new HashMap<>();
paramMap.put("city", "北京");
String result= HttpUtil.post("https://www.heckjj.com", paramMap);

//文件上传只需将参数中的键指定(默认file),值设为文件对象即可,对于使用者来说,文件上传与普通表单提交并无区别
paramMap.put("file", FileUtil.file("D:\face.jpg"));
String result= HttpUtil.post("https://www.heckjj.com", paramMap);
对应源码:


/**
     * 发送post请求
     *
     * @param urlString 网址
     * @param paramMap post表单数据
     * @return 返回数据
     */
    public static String post(String urlString, Map<String, Object> paramMap) {
        return HttpRequest.post(urlString).form(paramMap).execute().body();
    }

    /**
     * 发送post请求
     *
     * @param urlString 网址
     * @param params post表单数据
     * @return 返回数据
     */
    public static String post(String urlString, String params) {
        return HttpRequest.post(urlString).body(params).execute().body();
    }

二、HttpRequest与HttpResponse
  不满足于高度化的工具类封装,想拥有更多自定义请求与响应处理,可以使用HttpRequest与HttpResponse

快速上手:
String result2 = HttpRequest.post(url)
    .header(Header.USER_AGENT, "Hutool http")
    .form(paramMap)
    .execute().body();
11月30
原因:SpringBoot内嵌web容器,其特点是只有一个jar文件,在容器启动后不会解压缩。

解决方式:

1. 必须使用相对路径读取文件;

假设你的模板文件放在了 resources —> templates —> test.xlsx

2. 只能使用流去读取,不能用file;

  // jar里面文件读取方式:
  ClassPathResource classPathResource = new ClassPathResource("templates/test.xlsx");
  // 获取文件流
  classPathResource .getInputStream();

如果要将流存储到数组中如下:

  /**
     * 输出流转字节数组
     * @param input 输出流
     * @return 字节数组
     */
    public static byte[] toByteArray(InputStream input) throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024 * 4];
        int size = 0;
        while (-1 != (size = input.read(buffer))) {
            output.write(buffer, 0, size);
        }
        return output.toByteArray();
    }
11月27
Archetype是用于创建项目的骨架(或者模板),通过Archetype我们可以创建类似的Maven工程,同时Archetype能够极大的简化我们创建一个工程的步骤和流程。这里我将介绍自定义Maven的工程模板Archetype的方法和流程,这里采用的方法是从现有工程创建工程模板。

关于Archetype介绍参考:Introduction to Archetypes

一、Archetype自定义流程:
1、创建模板工程:找到一个现有的项目,进行编辑,将项目中的包结构、各类文件放置到合适的位置;



2、从模板工程创建Archetype:打开cmd窗口,切换当前目录到上面的工程目录下,执行maven命令:

mvn archetype:create-from-project
执行完成后,target/generated-sourced/archetype目录下就是我们需要的项目模板。

3、安装Archetype到本地仓库:cd进入target/generated-sourced/archetype中,执行命令:

mvn -Dmaven.test.skip=true clean install
将自定义Archetype安装到本地仓库即可:此时,自定义archetype就被安装到settings.xml中<localRepository>指定的本地仓库中,我的机器本地目录是D:/q/repos-maven;另外,archetype安装到本地仓库后,会在.m2/archetype-catalog.xml中加入对应的archetype节点,结构如下:

<archetype>

      <groupId>com.heckjj.blog</groupId>

      <artifactId>crm-archetype-archetype</artifactId>

      <version>1.0.0</version>

      <description>The parent pom of crm</description>

    </archetype>



4、使用自定义Archetype创建工程:

这样我们就创建成功了自定义的Archetype,可以通过命令从本地模板创建工程:

mvn archetype:generate -DarchetypeCatalog=local
以上就是创建自定义Archetype的简单流程。
11月24

PF4J的了解和使用

22:09编程杂谈  From: 本站原创
在平时编码过程中我们都知道要抽象,要封装变化,要实现开闭原则,比如对于很多相似的功能,我们可以将通用的功能抽象出来,然后把变化的不同的地方提取出去,比如模版模式、策略模式等都是实现类似的效果

比如对于策略模式,我们通常是定义一个接口,然后有不同的实现,这种是可以的,但是如果通用流程中要扩展的点较多的话,这些不同的实现也需要管理,可以把他们合并到一个单独的包中,再进一步,我们甚至可以将包单独提取出来,支持运行时加载包实现新增功能的支持

JDK对此功能的支持就是 SPI,但是它的限制较多,也不够灵活,比如dubbo就是自己定义了一套SPI的实现,这次我们来看另一个实现,pf4j 提供一套在基本框架中定义扩展点接口,然后通过不同的插件来实现扩展点的功能,来支持对新增开放对修改关闭

下面我们就来学习一下它的使用

使用
比如我们有一套通用的流程,假设是下单流程,不同的业务线等对于下单都有一些特殊点,但是它们的基本流程是相似的,这时候我们就可以先定义好通用的流程,不同的地方预留出扩展点接口,使用 pf4j 的流程如下

先定义好扩展点接口(需要定义单独的包,因为基本应用和各个扩展点的包都依赖它)
定义单独的插件包,其中实现扩展点接口的功能
在应用中编写基本流程和扩展点的发现使用功能
这次我们就参考 pf4j 提供的例子来看一下

1. 定义扩展点接口
pom.xml首先声明依赖

<dependency>
    <groupId>org.pf4j</groupId>
    <artifactId>pf4j</artifactId>
    <version>3.6.0</version>
    <!-- 一般应用中会依赖这个包,所以这里设置为provided即可 -->
    <scope>provided</scope>
</dependency>
之后即可声明各个扩展点接口

/**
* 假设我们需要一个通知用户的功能
* 需要注意的是,我们一定要继承 ExtensionPoint 接口,表示这是一个扩展点
*/
public interface Notice extends ExtensionPoint {
    boolean notice(List<Long> userIds);
}
11月24
PF4J是一个Java轻量级的插件框架,可以实现动态加载,执行,卸载外部插件(支持jar以及zip),具体可以看官网:https://pf4j.org/。

本文例子基于Github地址:https://github.com/pf4j/pf4j

<dependency>
  <groupId>org.pf4j</groupId>
  <artifactId>pf4j</artifactId>
  <version>3.6.0</version>
</dependency>
插件项目会涉及到3个工程:工程结构
plugin-api:定义可扩展接口
plugins:插件项目,可以包含多个插件,需要实现plugin-api中定义的接口
plugin-app:主程序,需要依赖plugin-api,加载并执行plugins
定义可扩展接口(plugin-api)
简单定义一个接口,需继承ExtensionPoint:

package plugin.api;

import org.pf4j.ExtensionPoint;

public interface Greeting extends ExtensionPoint {

    String getGreeting();
}
实现插件(plugins)
插件需要实现plugin-api定义的接口,并且使用@Extension标记:
package plugins;

import org.pf4j.Extension;
import plugin.api.Greeting;

@Extension
public class WelcomeGreeting implements Greeting {

    public String getGreeting() {
        return "Welcome";
    }
}

插件打包(plugins)
插件打包时,需要往MANIFEST.MF写入插件信息,此处使用maven插件(打包命令为package):
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <version>2.3.1</version>
  <configuration>
    <archive>
      <manifestEntries>
        <Plugin-Id>welcome-plugin</Plugin-Id>
        <Plugin-Version>0.0.1</Plugin-Version>
      </manifestEntries>
    </archive>
  </configuration>
</plugin>
11月23

MYSQL的监控方式

19:45数据库  From: 本站原创
对于当前数据库的监控方式有很多,分为数据库自带、商用、开源三大类,每一种都有各自的特色;而对于 mysql 数据库由于其有很高的社区活跃度,监控方式更是多种多样,不管哪种监控方式最核心的就是监控数据,获取得到全面的监控数据后就是灵活的展示部分。那我们今天就介绍一下完全采用 mysql 自有方式采集获取监控数据,在单体下达到最快速、方便、损耗最小。本次文章完全使用 mysql 自带的 show 命令实现获取,从 connects、buffercache、lock、SQL、statement、Database throughputs、serverconfig7 大方面全面获取监控数据。

1 连接数(Connects)

  • 最大使用连接数:show status like ‘Max_used_connections’

  • 当前打开的连接数:show status like ‘Threads_connected’

2 缓存(bufferCache)

  • 未从缓冲池读取的次数:show status like ‘Innodb_buffer_pool_reads’

  • 从缓冲池读取的次数:show status like ‘Innodb_buffer_pool_read_requests’

  • 缓冲池的总页数:show status like ‘Innodb_buffer_pool_pages_total’

  • 缓冲池空闲的页数:show status like ‘Innodb_buffer_pool_pages_free’

  • 缓存命中率计算:(1-Innodb_buffer_pool_reads/Innodb_buffer_pool_read_requests)*100%

  • 缓存池使用率为:((Innodb_buffer_pool_pages_total-Innodb_buffer_pool_pages_free)/Innodb_buffer_pool_pages_total)*100%

3 锁(lock)

  • 锁等待个数:show status like ‘Innodb_row_lock_waits’

  • 平均每次锁等待时间:show status like ‘Innodb_row_lock_time_avg’

  • 查看是否存在表锁:show open TABLES where in_use>0;有数据代表存在锁表,空为无表锁

备注:锁等待统计得数量为累加数据,每次获取得时候可以跟之前得数据进行相减,得到当前统计得数据

4 SQL

  • 查看 mysql 开关是否打开:show variables like ‘slow_query_log’,ON 为开启状态,如果为 OFF,set global slow_query_log=1 进行开启

  • 查看 mysql 阈值:show variables like ‘long_query_time’,根据页面传递阈值参数,修改阈值 set global long_query_time=0.1

  • 查看 mysql 慢 sql 目录:show variables like ‘slow_query_log_file’

  • 格式化慢 sql 日志:mysqldumpslow -s at -t 10 /export/data/mysql/log/slow.log
    注:此语句通过 jdbc 执行不了,属于命令行执行。
    意思为:显示出耗时最长的 10 个 SQL 语句执行信息,10 可以修改为 TOP 个数。显示的信息为:执行次数、平均执行时间、SQL 语句

备注:当 mysqldumpslow 命令执行失败时,将慢日志同步到本地进行格式化处理。

5 statement

  • insert 数量:show status like ‘Com_insert’

  • delete 数量:show status like ‘Com_delete’

  • update 数量:show status like ‘Com_update’

  • select 数量:show status like ‘Com_select’

6 吞吐(Database throughputs)

  • 发送吞吐量:show status like ‘Bytes_sent’

  • 接收吞吐量:show status like ‘Bytes_received’

  • 总吞吐量:Bytes_sent+Bytes_received

7 数据库参数(serverconfig)

show variables

8 慢 SQL

慢 SQL 指的是 MySQL 慢查询,具体指运行时间超过 long_query_time 值的 SQL。
我们常听 MySQL 中有二进制日志 binlog、中继日志 relaylog、重做回滚日志 redolog、undolog 等。针对慢查询,还有一种慢查询日志 slowlog,用来记录在 MySQL 中响应时间超过阀值的语句。慢 SQL 对实际生产业务影响是致命的,所以测试人员在性能测试过程中,对数据库 SQL 语句执行情况实施监控,给开发提供准确的性能优化意见显得尤为重要。那怎么使用 Mysql 数据库提供的慢查询日志来监控 SQL 语句执行情况,找到消耗较高的 SQL 语句,以下详细说明一下慢查询日志的使用步骤:
  • 确保打开慢 SQL 开关 slow_query_log

  • 设置慢 SQL 域值 long_query_time
    这个 long_query_time 是用来定义慢于多少秒的才算 “慢查询”,注意单位是秒,我通过执行 sql 指令 set long_query_time=1 来设置了 long_query_time 的值为 1, 也就是执行时间超过 1 秒的都算慢查询。

  • 查看慢 SQL 日志路径


  • 通过慢 sql 分析工具 mysqldumpslow 格式化分析慢 SQL 日志
    mysqldumpslow 慢查询分析工具,是 mysql 安装后自带的,可以通过./mysqldumpslow —help 查看使用参数说明

常见用法:
  1. 取出使用最多的 10 条慢查询
    ./mysqldumpslow -s c -t 10 /export/data/mysql/log/slow.log

  2. 取出查询时间最慢的 3 条慢查询
    ./mysqldumpslow -s t -t 3 /export/data/mysql/log/slow.log

注意:使用 mysqldumpslow 的分析结果不会显示具体完整的 sql 语句,只会显示 sql 的组成结构;
假如: SELECT FROM sms_send WHERE service_id=10 GROUP BY content LIMIT 0, 1000;
mysqldumpslow 命令执行后显示:
Count: 2 Time=1.5s (3s) Lock=0.00s (0s) Rows=1000.0 (2000), vgos_dba[vgos_dba]@[10.130.229.196]SELECT 
FROM sms_send WHERE service_id=N GROUP BY content LIMIT N, N
mysqldumpslow 的分析结果详解:
  • Count:表示该类型的语句执行次数,上图中表示 select 语句执行了 2 次。

  • Time:表示该类型的语句执行的平均时间(总计时间)

  • Lock:锁时间 0s。

  • Rows:单次返回的结果数是 1000 条记录,2 次总共返回 2000 条记录。

通过这个工具就可以查询出来哪些 sql 语句是慢 SQL,从而反馈研发进行优化,比如加索引,该应用的实现方式等。常见慢 SQL 排查
  1. 不使用子查询
    SELECT FROM t1 WHERE id (SELECT id FROM t2 WHERE name=’hechunyang’);
    子查询在 MySQL5.5 版本里,内部执行计划器是这样执行的:先查外表再匹配内表,而不是先查内表 t2,当外表的数据很大时,查询速度会非常慢。
    在 MariaDB10/MySQL5.6 版本里,采用 join 关联方式对其进行了优化,这条 SQL 会自动转换为 SELECT t1.
     FROM t1 JOIN t2 ON t1.id = t2.id;
    但请注意的是:优化只针对 SELECT 有效,对 UPDATE/DELETE 子 查询无效, 生产环境尽量应避免使用子查询。

  2. 避免函数索引
    SELECT FROM t WHERE YEAR(d) >= 2016;
    由于 MySQL 不像 Oracle 那样⽀持函数索引,即使 d 字段有索引,也会直接全表扫描。
    应改为 > SELECT 
    FROM t WHERE d >= ‘2016-01-01’;

  3. 用 IN 来替换 OR 低效查询
    慢 SELECT FROM t WHERE LOC_ID = 10 OR LOC_ID = 20 OR LOC_ID = 30;
    高效查询 > SELECT 
    FROM t WHERE LOC_IN IN (10,20,30);

  4. LIKE 双百分号无法使用到索引
    SELECT FROM t WHERE name LIKE ‘%de%’;
    使用 SELECT 
    FROM t WHERE name LIKE ‘de%’;

  5. 分组统计可以禁止排序
    SELECT goods_id,count() FROM t GROUP BY goods_id;
    默认情况下,MySQL 对所有 GROUP BY col1,col2… 的字段进⾏排序。如果查询包括 GROUP BY,想要避免排序结果的消耗,则可以指定 ORDER BY NULL 禁止排序。
    使用 SELECT goods_id,count (
    ) FROM t GROUP BY goods_id ORDER BY NULL;

  6. 禁止不必要的 ORDER BY 排序
    SELECT count(1) FROM user u LEFT JOIN user_info i ON u.id = i.user_id WHERE 1 = 1 ORDER BY u.create_time DESC;
    使用 SELECT count (1) FROM user u LEFT JOIN user_info i ON u.id = i.user_id;

9 总结

  • 任何东西不应过重关注其外表,要注重内在的东西,往往绚丽的外表下会有对应的负担和损耗。

  • mysql 数据库的监控支持通过 SQL 方式从 performance_schema 库中访问对应的表数据,前提是初始化此库并开启监控数据写入。

  • 对于监控而言,不在于手段的多样性,而需要明白监控的本质,以及需要的监控项内容,找到符合自身项目特色的监控方式。

  • 在选择监控工具对 mysql 监控时,需要关注监控工具本身对于数据库服务器的消耗,不要影响到其自身的使用。

分页: 2/63 第一页 上页 1 2 3 4 5 6 7 8 9 10 下页 最后页 [ 显示模式: 摘要 | 列表 ]