如何通过反射从HttpServletRequest获取到HttpServletResponse对象(Tomcat限定


起因

项目会记录每次请求的进出便于调试和跟踪, 具体的实现是使用ServletRequestListenser的requestInitialized()和requestDestroyed()方法. 然后我们现在希望能够在请求出的时候同时记录到返回的状态码等信息, 这就需要获取到HttpServletResponse对象.

当然这个需求可能从一开始就是伪需求, 因为从Tomcat7开始就有提供AccessLog的记录, 使用Ngnix的访问记录也比在代码中去记录要好, 不过因为历史原因暂且就这样了╮( ̄▽ ̄)╭

发展

ServletRequestListener中的两个方法只提供了ServletRequestEvent参数, 然而你只能从这个类中获取到ServletRequest和ServletContext两个接口, 而在两个接口中是没有提供任何访问到Response的方法的, 因为这是由javax.serlvet包提供的接口.

然而事实上, 如果你使用的是Tomcat容器的话, 会发现这个接口在运行时的实现类是org.apache.catalina.connector.RequestFacade, 直接通过这个类我们依然获取不到Response, 但是它有一个类型为org.apache.catalina.connector.Request的属性, 而这个类他有一个getResponse的方法, 因此我们能通过这个类获取到Response对象.

1.png

事实上, org.apache.catalina.connector是位于tomcat根目录下lib目录下的catalina.jar, 出于部署方便的考虑, 我并不希望直接在工程中引入这个包, 所以就只能用万能的反射辣(x

Talk is Cheap. Show me the code.

    /**
     * 从Request中获取对应的Response的StatusCode
     * 采用了反射的方法, 可能只在限定版本的Tomcat中可用
     *
     * @param req Tomcat中的HTTPServletRequest对象
     * @return 提取出来的statusCode, 如果为-1, 说明提取出错
     */
    private Integer getStatusCode(HttpServletRequest req) {
        try{
            Field reqField = req.getClass().getDeclaredField("request");
            reqField.setAccessible(true);
            // org.apache.catalina.connector.Request
            Object reqObj = reqField.get(req);
            // org.apache.catalina.connector.Response
            Object resObj = reqObj.getClass().getDeclaredMethod("getResponse").invoke(reqObj);
            return (Integer)resObj.getClass().getDeclaredMethod("getStatus").invoke(resObj);
        }catch(NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e){
            e.printStackTrace();
        }
        return -1;
    }

总结

其实这是一个很尬而且很难受的操作, 但是有的时候出于各种考虑又需要进行这样的操作. 这个时候反射就是我们的好朋友啦.

事实上, 从调用栈可以看到Tomcat在connector中的service()方法中就会创建该次请求的request与response, 并互相关联. 只不过javax.servlet.http包中的接口没有给出相应的方法也是很尴尬→_→

    public void service(Request req, Response res) throws Exception {
        org.apache.catalina.connector.Request request = (org.apache.catalina.connector.Request)req.getNote(1);
        org.apache.catalina.connector.Response response = (org.apache.catalina.connector.Response)res.getNote(1);
        if (request == null) {
            request = this.connector.createRequest();
            request.setCoyoteRequest(req);
            response = this.connector.createResponse();
            response.setCoyoteResponse(res);
            request.setResponse(response);
            response.setRequest(request);
            req.setNote(1, request);
            res.setNote(1, response);
            req.getParameters().setQueryStringEncoding(this.connector.getURIEncoding());
        }
        //...
    }

记一次神奇的Manjaro文件丢失修复


事件起因

上次关闭了虚拟机之后, 手贱在Vmware里使用了"压缩磁盘以回收未使用的空间", 然后下次再启动的时候, 启动就报错了...
QQ图片20180808090659.jpg

故障现象

/sbin/init提示找不到libz.so.1文件找不到.
QQ图片20180808090624.png

故障分析

libz.so.1是zlib的库文件, 在系统中肯定是安装了的.

  • 联想到上次关机前, 我滚了一次系统, 首先想到是不是Arch又日常滚挂了, 于是打开了Arch官网...可是没有任何关于此的通知, 相比应该不是滚系统的原因.
  • 联想到我关机之后压缩了一下磁盘, 第二反应是不是文件系统挂了, 于是启动了livecd, 跑起了brtfsck, 然而它告诉我其实什么问题也没有.

恢复过程

于是这就很尴尬了...
既然文件系统本身没问题, 那就只好先恢复系统再说, 于是在livecd里把磁盘挂载, 然后find -name "libz.so.1"...
QQ图片20180808090634.png
诶...怎么只在/usr/lib32/下有libz.so.1, 看来应该是/usr/lib/下的文件丢失了导致的.
嗯 复制livecd的libz.so.1到/usr/lib/, 成功启动.

然后我需要重装一下zlib, 于是pacman -S zlib启动!
QQ图片20180808090641.png
emmmm 我的pacman发生了什么!
看来不仅仅是丢了一个libz.so.1的问题...好像zlib的软件包元数据也丢了Orz
更神奇的是, 我安装其他软件都没问题, 唯独zlib...emmmmm

群里有大佬建议说, 你不如用pacstrap安装一下试试看, 于是...
QQ图片20180808090648.png
emmmmm 还是不行啊Orz

思考了一下, 你不是说desc文件找不到么, 于是我去那个目录看了一下
emmmm 好像只剩下一个mtree文件了, desc和files都丢掉了...
于是我想要不干脆把这个文件夹删掉, 是不是就当作我从来没装过zlib了.
立刻mv /var/lib/pacman/local/zlib-1:1.2.11-3 ~/ && pacman -S zlib
QQ截图20180808093307.png
emmmm 好像是真的 虽然提示文件系统中已存在 这当然是因为我们已经安装过的原因

不过可以这样 pacman -S --dbonly zlib~
QQ截图20180808093429.png
成功! 这个时候再去pacman -S zlib已经一点问题也没有了yeah

总结与后记

  • pacman的软件包数据库真简单啊, 除了mtree是未知格式以外, desc和files都是纯文本文件
  • pacman的单个软件包数据库挂了完全一点都不影响到整个软件包仓库
  • 恢复完之后, 我发现上次关机前我滚过的包, 它又提醒我再滚一遍, 仿佛是一段时间内的磁盘写入完全丢失, 甚至还丢了别的文件
  • 如果真的丢了别的文件, 其实我也看不出来Orz
  • 所以既然用了brtfs, 那么就要常做snapshot啊_(:3」∠)_
  • 所以下次再也不手贱压缩磁盘了!!!

以上


使用Matplotlib画图时如何修改字体及显示汉字


前情提要

Matplotlib是python下一个非常常用的绘图库, 可是如果你用pip install matplotlib默认安装后, 却会发现它会把你输入的中文变成框框.
原因其实很简单, 只是因为默认的Matplotlib安装不带中文字体而已, 所以我们需要将添加中文字体进去.
当然还有可能的原因是你用的是python2, 对于utf-8的中文文本要进行一些别的非常恶心的操作, 当然这个不是本文的讨论范围.
以下文字的操作环境是Windows SubSystem for Linux, 但是原理在各个平台应该是通用的.

如何添加中文字体到Matplotlib中

Matplotlib的字体无论是在Linux还是在Windows中, 都和系统字体是完全独立的, 因此你需要手动将中文复制到其字体路径中去.
具体的路径位置和你当前使用python的site-packages路径有关, 在Linux中这个路径有可能在/usr/lib/python*下也有可能在~/.local/lib/python*下, 而在Windows中这个路径应该在你的python安装路径下.
那么具体的字体文件夹路径在哪里呢?假设$site-packages是你的site-packages路径, 那么字体目录在$site-packages/matplotlib/mpl-data/font下. 当然这个目录下一般会有afm pdfcorefonts ttf三个文件夹, 一般我们添加的都是ttf字体, 因此一般是添加到该目录下. 注意ttc字体放在这个目录下并不能被识别
比较推荐的代码字体有: 更纱黑体, 思源黑体.
更纱黑体是某个在微软工作的大佬正在开发而且不断在维护的一个字体, 效果很棒.
思源黑体是Adode开源的一个字体, 对于汉字和字幕都有着很好的效果.
总体来说, 这两个字体的显示效果差不多, 细节上略有差别, 具体的细节可以戳这里.

清空字体缓存

rm -rf ~/.cache/matplotlib

只有正确清空了字体缓存, 你的字体才会被Matplotlib再次识别. 对于Windows, 这个路径可能在C:\Users\$username\.matplotlib下.

查看已经添加在Matplotlib中的字体名

from matplotlib.font_manager import FontManager

fm = FontManager()
mat_fonts = set(f.name for f in fm.ttflist)
print(mat_fonts)

在Matplotlib中是使用字体名来配置字体的, 而不是字体的文件名, 因此你需要是从这里读到的字体名来进行下面的配置, 同时也可以查看之前的添加字体操作是否成功.

修改Matplotlib的字体配置

修改Matplotlib的字体配置, 有两种方法:
一种是我们可以在代码中修改配置;
另外一种是我们可以去修改Matplotlib的配置文件;

在代码中修改配置

通过plt.rcParams['$config_item']就可以修改Matplotlib的字体配置选项.
Matplotlib中一共有五种类型的字体, 分别是:

plt.rcParams['font.serif']=['Sarasa Mono SC'] # 衬线字体
plt.rcParams['font.sans-serif']=['Sarasa Mono SC'] # 无衬线字体
plt.rcParams['font.cursive']=['Sarasa Mono SC'] # 手写字体
plt.rcParams['font.fantasy']=['Sarasa Mono SC'] # 梦幻字体
plt.rcParams['font.monospace']=['Sarasa Mono SC'] # 等宽字体

直接修改配置文件

通过下面的代码可以读取到matplotlib的配置文件, 当然这个配置文件一般在$site-packages/matplotlib/mpl-data/matplotlibrc下:

import matplotlib

print(matplotlib.matplotlib_fname())

在这个文件搜索font.serif就会看到之前说的五大字体族了, 去掉前面的#号注释, 再在:前加上你的字体名就可以添加字体了的说.

最后的效果如图所示

QQ截图20180511151753.png


在WSL运行32位应用程序的神奇操作


在WSL运行32位应用程序的神奇操作

原理

主要的操作是通过qemu-i386-static模拟来运行x32程序, 然后通过binfmt来指定使用qemu来运行给定x32二进制文件

首先需要开启debian的x32基础支持

dpkg --add-architecture i386
apt-get update
apt-get install libstdc++6:i386

这会在dpkg中添加i386架构, 这样就可以通过apt工具安装i386的包了, 然后安装libstdc++会自动安装libc6/libgcc_s, 这样就可以运行基本的x32程序了.
一般的Linux到这一步就已经可以运行x32的程序了, 但是对于WSL来说还是不行, 使用ldd exec依然会告诉你这不是一个可执行文件, 于是说我们需要安装qemu.

安装qemu

apt-get update
apt-get install qemu-user-static

这样我们就可以通过qemu-i386-static exec来实际的运行我们想要的x32程序了, 但是这样很麻烦, 于是我们可以配置binfmt来自动使用qemu来运行qemu.

配置binfmt

update-binfmts --install i386 /usr/bin/qemu-i386-static --magic '\x7fELF\x01\x01\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x03\x00\x01\x00\x00\x00' --mask '\xff\xff\xff\xff\xff\xff\xff\xfc\xff\xff\xff\xff\xff\xff\xff\xff\xf8\xff\xff\xff\xff\xff\xff\xff'
service binfmt-support start

这样, 我们就可以像在正常Linux里面一样运行x32的程序了. 真是非常神奇的操作_(:3」∠)_

后记

当然每次启动要记得启动一下binfmt服务才行.

service binfmt-support start

Reference: https://github.com/Microsoft/WSL/issues/2468#issuecomment-374904520


博客又又又搬家了&VPS折腾记录


搬迁到新的VPS

现在的博客已经从之前的Banwagon搬迁到了UltraVPS上, 虽然年付用从$19变成了$40, 但是依然感觉很值:
一方面是线路很棒, LA的QN机房, IPV6线路到达LA之后只要四跳总跳数是14跳, IPV4线路也是差不多的, 但是因为我这边要先去北京赛尔的机房所以总跳数是17跳.
另外一方面是配置感人, 双核2G+(50G+10G)SSD, 这个配置只要$3.33每月真的感觉很难找到第二家了, 何况还是KVM, 线路质量也好, 也支持IPV6, 简直完美.

为什么会是(50G+10G)呢...
这是因为我第一次见到有商家虽然说是50G, 但是其实另外给了你10G用来装系统, 然后50G是完全的数据分区的...
于是我用了btrfs, 将这两个虚拟硬盘做成了一个逻辑卷嗯[Btrfs大法好

详细的评测+安利可以看这篇博文: https://blog.cysi.me/2018/03/new-vps-ultravps-providerservice.html
我当初就是看了这个之后心动的_(:3」∠)_
50G的SSD虽然不多, 但是对于自用的Seafile完全足够, 而且2G RAM对于Seafile Pro的配置条件也完全满足, 嗯
所以现在这上面部署了我的博客和Seafile还有你懂的服务.

购买的话, 这家似乎支持PayPal, 然后反欺诈还蛮严格的, 请务必填上看起来没问题的联系信息, 不然订单不会通过审核的.

具体的使用情况的话, 可能是开会已经开完了, 所以SSH上去不会突然就断掉, 也没有SSH很卡的情况, 虽然没法和本地路由器比, 但是也还是可以接受的.
说起来, 这家的KVM居然可以通过cat /proc/cpuinfo看到CPU类型, 是E5-2670.
看起来似乎母鸡是多CPU的, 不过很迷啊, 为什么CPU是这么老旧的一代E5...这个CPU不是都上岸多年了吗...
不过反正便宜就不管那么多了, 而且我真的没有任何需要CPU性能的服务.
IO的话...大概读写240M/s的样子, emmm凑合吧, 毕竟便宜.
ping值从170ms-240ms不等, HTTP下载速度的话, 大概最快可以接近4M/s...
不过主要还是看脸的, 线路路由会随时间而变化= =
比如说一开始是从武汉移动从上海出国的, 然后现在就变成了走教育网从北京出国...
总而言之, 这么便宜, 又能满足各项需求, 很适合自己的情况, 还要什么自行车啊.

最近试用了一下Syncthing...不得不说线路质量真是棒啊_(:3」∠)_
在IPV4环境下的上下行带宽:
QQ截图20180410133805.png
上下行感觉很明显是被限制在20Mbps的样子...
看了一下路由感觉是走了学校的电信出口, 所以可能是在学校的出口被限速了吧应该
在IPV6环境下的上下行带宽:
QQ截图20180410133204.png
这个上行带宽真的是吓哭我了, 第一次在公网见到这么高速率好吗!
说起来这个上行带宽的上升曲线, 真是生动形象的TCP慢启动过程啊233
下行的带宽虽然不及上行带宽, 但是还是非常可以的.
明明服务器上用了BBR, 上下行的路由还是一样的, 为什么带宽差这么多呢_(:3」∠)_
不是很懂(有可能是网络环境的影响吧, 比如说被大量PT流量给拥塞了什么的

博客搬迁

博客系统还是Typecho, 这货在死了几年以后居然STM的诈尸了. 不过更新之后, 多了一个很棒的功能就是备份还原.
这样博客的搬迁工作就非常简单了, 只要在原博客上备份, 再来新博客上还原就好, 也不用倒腾数据库了, 而且似乎我也没啥文件的样子...

然后过了这么几年, 我也从服务器小白逐渐进阶, 原来用的是LNMP的一键安装包, 现在还是觉得手动配置Nginx和PHP比较好.
然后网站前几天也弄了全站HTTPS, 用的是Let's encrypt的证书啦, 不过因为用了cloudflare的CDN, 所以拿到的证书可能是cf的共享证书...
然后用了大佬的自动部署脚本: https://github.com/gileshuang/letsautoencrypt
这个站点还有多个备份域名, 比如说:
http://www.slkun.me / http://blog.slkun.me
http://www.doris.work / http://blog.doris.work
http://www.lovedoris.moe / http://blog.lovedoris.moe
= =嗯 那个lovedoris.moe是没有cf的CDN部署的
然后哦Nginx的部署其实还是折腾了一下的= =因为其实对Nginx并不熟悉, 配置如下需要的可以参考:

server {
    listen       localhost:port;
    server_name  domainName;

    access_log  /nginx/logs/typecho.access.log;
    error_log  /nginx/logs/typecho.error.log;

    root        webRoot;
    index       index.php index.html index.htm;

    location / {
        if (!-e $request_filename) {
            rewrite ^(.*)$ /index.php$1 last;
        }
    }

    location ~ '\.php(/|$)' {
        include        fastcgi.conf;
        fastcgi_pass   unix:/run/php-fpm/php-fpm.sock;
        fastcgi_split_path_info       ^(.+\.php)(.*)$;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO       $fastcgi_path_info;
    }

    # redirect server error pages to the static page /50x.html
    #error_page  404              /404.html;
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

VPS折腾史

在16年入手了Bandwagon的256M VPS以后, 突然看到这家又推出了PHOENIX AZ的Special套餐, 年付$18就可以有1G RAM+20G SSD, 于是脑子一热就入手了.
因为位置问题, 所以ping值是会高一些, 然后路由一般般吧. 性能的话...emmm Xean D-1540? 我第一次听说这个系列的志强= = IO还是一如既往的优秀平均500+M/s

本来之前用这个256M的主要是因为这款的机房可切换, 万一要是ip翻车我还可以换个机房继续用, 可是用了一年多感觉非常稳, 完全没有任何问题.
但是以防万一, 所以这个256M的还是一直在用, 然后那个1G的因为硬盘大内存多, 于是我就在上面装了Seafile的服务, 用于文件同步和备份.
Seafile的话又是一个可以吹一波的好用软件, 这个的话以后再写好了.

后来, 正好之前那个256M的到期, 加上Banwagon推出了KVM的VPS, 然后还是联通和CN2直连的, 内存也是512M, 年付$28. 考虑到IPV6我可以用之前那个PHOENIX AZ, 而且PHOENIX AZ的那个在公网的使用体验不佳, 原来的256M一方面是OVZ而且内存太小, 于是就入手了这个.
其实也考虑过别的VPS商家, 比如说DigitalOcean, Vultr, Linode之类的, 但是最低$5/mo的价格实在是感人肺腑, 而且开了Vultr和DigitalOcean试用, 线路带宽实在堪忧, HTTP下载速度不超过100K/s, 这个就非常尴尬了. 所以就觉得Banwagon虽然便宜, 但是还慢靠谱的(对于我的网络环境来说)
这个CN2直连果然还是满靠谱的, 因为KVM所以并不清楚CPU的型号, 但是IO如上一样平均600+M/s吧. 线路的话, ping只有160ms左右吧, HTTP下载速度基本是不会低于500K/s的, 一般是1M/s左右, 好的话可以到2M/s.

随着照片什么的备份多了起来, 逐渐感觉20G SSD开始不够用了起来, 而且PHOENIX AZ的这个的速度突然变得非常慢, 又是HTTP下载速度不超过100K/s的节奏(确切的说是ipv6线路, ipv4线路好像还可以). 而且这个是OVZ的虚拟化, 没法换内核, 也就没法BBR, 很多L2TP之类的操作也没法弄, 于是就开始物色有没有存储更多价格差不多的VPS来替换.

正好看到小伙伴有推荐Virmach, 然后看了一下感觉这个价格真...
我本来以为$20年付已经非常便宜了...没想到还是$10年付的OVZ, 甚至还有$12.5年付的KVM...
我只感觉这家是不是分分钟要跑路啊, 再加上看到这个其实有点犹豫.
但是看到年付$35的512M RAM + 500G HDD瞬间就没了脾气, 决定先开一个月试试看, 结果没注意是备货状态= =
不过前几天也正式部署了, 虽然说是500G, 但其实只有465G, 感觉像是挂了一个500G的物理硬盘的感觉...不会是单盘吧= =
性能嘛...嗯 真TM感人...和当年我笔记本的那个超薄7mm硬盘差不多...IO读写不上百你敢信? 大概只有50-70M/s的样子...
网络情况...虽然是在NY, 虽然ping值很高, 但是HTTP下载还可以, 平均2M/s左右吧...所以还是有点可用性的
不过的话, 我已经有了50G SSD的那个了, 所以这个其实没啥需求了嗯

关于VPS的推荐和介绍, 可以看一下这篇博文, 写得挺全的: https://yorkchou.com/host-provider.html
然后后悔自己黑五的时候怎么没多关注一下_(:3」∠)_

所以现在手上有三个VPS, 到时候可能那两个Bandwagon就不续费了, 然后考虑弄一个日本来玩嗯(如果Vultr$2.5有货的话)