0%

首先我们要取得 HttpServletRequest (HttpServletResponse同理)

方式一:通过静态方法获取,也可以封装一个静态方法出来

1
2
3
4
5
6
7
public class HttpContextUtils
{
public static HttpServletRequest getHttpServletRequest()
{
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
}

方式二:通过参数直接获取,只要在你的方法上加上参数,SpringBoot 就会帮你绑定,你可以直接使用。如果你的方法有其他参数,把 request 参数加到后面即可

1
2
@GetMapping("/one")
public void methodOne(@RequestParam String name, HttpServletRequest request) { }

方式三:注入到类,这样就不用每个方法都写了

1
2
3
4
5
6
7
8
9
10
private final HttpServletRequest request;

public TestController(HttpServletRequest request)
{
this.request = request;
}

@Log("执行方法一")
@GetMapping("/one")
public void methodOne(@RequestParam String name) { }

拿到 HttpServletRequest 后即可取得请求的 IP 地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class IPUtils
{
/**
* 获取IP地址
* <p>
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
*/
public static String getIpAddr(HttpServletRequest request)
{

String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
{
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
}

Docker 操作指北

一)常用命令


拉取 Docker 镜像
1
docker pull image_name

我们通过最简单的 image 文件 “hello world”,感受一下 Docker

因为国内连接 Docker 的官方仓库很慢,因此我们在日常使用中会使用 Docker 中国加速器。通过 Docker 官方镜像加速,中国区用户能够快速访问最流行的 Docker 镜像。该镜像托管于中国大陆,我们配置国内镜像可以享受到更快的下载速度和更强的稳定性,从而能够更敏捷的开发和交付 Docker 化应用。

Docker 中国官方镜像加速可通过 registry.docker-cn.com 访问。该镜像只包含流行的公有镜像,私有镜像仍需要从美国镜像中拉去。

修改系统中 Docker 对应的配置文件即可,如下包含了公司仓库和一些其他国内镜像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"debug": true,
"experimental": false,
"insecure-registries": [
"registry.becypress.com"
],
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://hub-mirror.c.163.com",
"https://reg-mirror.qiniu.com",
"http://f1361db2.m.daocloud.io",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com"
]
}
win10配置方式:

右键电脑右下角的Docker 图标–>Settings–>Docker Engine—> 将加速器地址复制到该页面上的文本框中,点击Apply 然后等待Docker重启,重启完毕就可以使用新的Docker镜像源了!!!

其他 windows 配置方式:
1
2
3
4
5
6
7
8
# 进入 Docker 虚拟机
docker-machine.exe ssh default
# 编辑 Docker 配置文件
sudo vi /etc/docker/daemon.json
# 按下 i 进入编辑模式添加以上配置
# 按下 : 退出编辑模式
# 按下 wq 保存并退出
# 退出虚拟机按下 exit

linux 系统配置方式:

配置好后运行如下命令,将 image (镜像) 文件从仓库抓去到本地:

1
docker image pull library/hello-world

上面的代码中,docker image pull 是抓取 image 文件的命令。library/hello-worldimage 文件在仓库里面的位置,其中 libraryimage 文件所在的组,hello-worldimage 文件的名字。

查看宿主机上的镜像,Docker 镜像保存在 /var/lib/docker 目录下
1
docker images [OPTIONS] [REPOSITORY[:TAG]]

OPTIONS 说明:

  • -a:列出本地所有的镜像(含中间映像蹭,默认情况下,过滤调中间映像层);
  • –digests:显示镜像的摘要信息;
  • -f:显示满足条件的镜像;
  • –format:指定返回值的模版文件;
  • –no-trunc:显示完整的镜像信息;
  • -q:只显示镜像ID;

hello-world 镜像抓取成功后,就可以在本机看到这个 image 文件了。

运行镜像:会创建一个新的容器并运行
1
docker run [OPTIONS] image_name/image_id [COMMAND] [ARGS...]

OPTIONS 说明:

  • -a stdin:指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
  • -d:后台运行容器,并返回容器ID;
  • -i:以交互模式运行容器,通常与 -t 同时使用;
  • -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;
  • -P:随机端口映射,容器内部端口随机映射到主机的高端口;
  • -p:指定端口映射,格式为:主机(宿主)端口:容器端口
  • –name:为容器指定一个名称;
  • –dns:指定容器使用的DNS服务器,默认和宿主一致;
  • –dns-search:指定容器DNS搜索域名,默认和宿主一致;
  • -h:指定容器的 hostname
  • -e:设置环境变量;
  • –env-file:从指定文件读入环境变量;
  • –cpuset:绑定容器到指定CPU运行;
  • -m:设置容器使用内存最大值;
  • –network:指定容器网络模式;
  • –ip:指定容器IP;
  • –network-alias:指定容器网络别名;
  • …:其他可通过 docker run –help 查看

现在运行 hello-world 这个镜像文件:

输出这段提示以后,hello-world 就会停止,容器自动终止。有些容器不会自动终止,因为提供的是服务,比如 OracleMysql镜像等。

查看容器
1
docker ps [OPTIONS]

OPTIONS 说明:

  • -a:显示所有的容器,包括未运行的;
  • -f:根据条件过滤显示的内容;
  • –format:指定返回值的模版文件;
  • -l:显示最近创建的容器;
  • -n:列出最近创建的n个容器;
  • –no-trunc:不截断输出;
  • -q:只显示容器ID;
  • -s:显示总的文件大小;

查看之前启动的 hello-world 容器:

输出详情介绍:

  • CONTAINER ID:容器ID;

  • IMAGE:使用的镜像;

  • COMMAND:启动容器运行时的命令;

  • CREATED:容器的创建时间;

  • STATUS:容器状态

    • created:已创建
    • restarting:重启中
    • running:运行中
    • removing:迁移中
    • paused:暂停
    • exited:停止
    • dead:死亡
  • PORTS:容器的端口信息和使用的连接类型(tcp/udp)

  • NAMES:自动分配的容器名称;

启动、停止、重启容器
1
2
3
docker start container_name/container_id
docker stop container_name/container_id
docker restart containcer_name/container_id

我们现在可以将 hello-world 容器启动:

进入容器
1
docker exec [OPTIONS] container_name/container_id COMMAND [ARG...]

OPRIONS 说明:

  • -d:分离模式,在后台运行
  • -i:即使没有附加也保持STDIN打开
  • -t:分配一个伪终端

通过 exec 命令对指定的容器执行 sh:

通过 exec 命令对指定的容器执行 bash:

查看容器日志
1
docker logs [OPTIONS] container_name/container_id

OPTIONS 说明:

  • -f:跟踪日志输出;
  • –since:显示某个开始时间的所有日志
  • -t:显示时间戳
  • –tail:仅列出最新的N条容器日志

删除容器
1
docker rm [OPTIONS] container_name/container_id

OPTIONS 说明:

  • -f:通过 SIGKILL 信号强制删除一个运行中容器;
  • -l:移除容器间的网络连接,而非容器本身;
  • -v:删除与容器相关的卷;

删除镜像
1
docker rmi [OPTIONS] image_name/image_id

OPTIONS 说明:

  • -f:强制删除;
  • –no-prune:不移除该镜像的过程镜像,默认移除;

查看当前系统 Docker 信息
1
docker info

二)Dockerfile


概念

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,这个脚本就是 Dockerfile

Dockerfile 是一个文本文件,其内包含了一条条的指令,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需要在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。

简单来说:Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

文件格式
1
2
3
4
5
6
7
8
# 1、第一行必须指定 基础镜像信息
FROM nginx:alpine
# 2、维护者信息
MAINTAINER docker_user docker_user@email.com
# 3、镜像操作指令
RUN echo '这是一个本地构建的nginx镜像' > /usr/share/nginx/html/index.html
# 4、容器启动执行指令
CMD /usr/sbin/nginx

Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如 RUN 指令。每执行一条 RUN 指令,镜像添加新的一层,并提交;最后是 CMD 指令,来指明运行容器时的操作命令。

注意Dockerfile 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层会造成镜像膨胀过大;

构建镜像

Dockerfile 文件的存放目录下,执行构建动作:

1
docker build [OPTIONS] PATH | URL | -

OPTIONS 说明:

  • -t / -tag:镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签;
  • –build-arg:设置镜像创建时的变量;
  • –cpu-shares:设置 cpu 使用权重;
  • -f:指定要使用的 Dockerfile 路径;
  • -m:设置内存最大值;
  • …其他可通过 docker build –help 查看

docker build -t nginx:test . 代表本次执行的上下文路径(上下文路径是指 docker 在构建镜像,有时候需要使用本机的文件,比如复制,docker build 命令得知这个路径后,会将路径下的所有内容打包)。

推送镜像
1
docker push [OPTIONS] NAME[:TAG]

OPTIONS:说明

  • –disable-content-trust:忽略镜像的校验,默认开启

指令详解
  • FROM:定制的镜像都是基于 FROM 的镜像,这里的 nginx 就是定制需要的基础镜像。后续操作都是基于nginx;

    • 格式:FROM <image>[:<TAG>]
  • RUN:用于执行后面跟着的命令,等同于在终端操作的 shell 命令;

    • 格式:RUN <command>
  • CMD:类似与 RUN 指令,用于运行程序,但两者运行的时间不同

    • CMD 是在 docker run 时运行,如果多个 CMD 指令仅最后一个生效
    • RUN 是在 docker build 时运行
  • COPY:复制指令,从上下文目录中复制文件或者目录到指定容器里的指定路径

    • 格式:COPY 源路径/文件 目标路径/文件
    • 案例:COPY ./target/service-caterer-2.0.0.war app.war
  • ADD:添加指令,和 COPY 指令使用格式一致;

  • ENV:设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量;

    • 格式:ENV <key> <value> 或者 ENV <key1>=<value1> <key2>=<value2>

    • 案例:

      1
      2
      3
      ENV NODE_VERSION 7.2.0

      RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz"
  • ARG:构建参数,与 ENV 作业一致。不过作用域不一样,ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程有效,构建好的镜像内不存在此环境变量。

    • 格式:ARG <参数名>[=<默认值>]
  • VOLUME:定义匿名数据卷,在启动容器的时候忘记挂载数据卷,会自动挂载到匿名卷。

    • 格式:VOLUME ["<路径1>","<路径2>"...]
    • 作用:避免重要的数据,因容器重启而丢失,这是非常致命的
  • EXPOSE:为构建的镜像设置监听端口,使容器在运行时监听。

    • 格式:EXPOSE <端口1> [<端口2>...]
    • 作用:帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;且镜像在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。
  • WORKDIR:指定工作目录。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在(WORKDIR 指定的工作目录,必须时提前创建好的)。

  • 格式: WORKDIR <工作目录路径>

  • USER:用于指定后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

    • 格式:USER <用户名>[:<用户组>]

三)Docker Compose

待完善。。。

前言

docker 获取镜像的方式除了 pull 到仓库之外还有一种是将本地镜像打包拷贝给其他宿主主机来运行。假设现实环境中本地仓库和远程仓库连接异常,那么我们将事先打包好的镜像分发给其他 docker 节点用也是一种解决方案。

具体操作步骤如下

1、执行如下命令找到被打包镜像的名字和版本号(版本号 = TAG)
1
docker images
2、docker 打包镜像的两种方式(选一种执行即可)
1
2
docker save 镜像名字:版本号 > /root/打包名字.tar
docker save -o /root/打包名字.tar 镜像名字:版本号
3、将打包的镜像上分法到其他宿主机的 /root/ 目录下
4、将打成 tar 报的镜像 load 出来
1
docker load < /root/打包名字.tar
5、查看 load 出来的镜像
1
docker images
6、如果 load 出来的镜像其名字和版本号均为 none,我们要通过 tag 命令赋予名字和版本号
1
docker tag 镜像ID 镜像名字:版本号

前言

最近项目中使用了 activiti 工作流,需要实现自定义流程,这样就需要通过代码创建流程中的各个节点和流转条件,但是用代码实现后生成到本地的 bpm 文件用 IDEA 中下载的插件 actiBPM 打开之后中文乱码。

解决方案

IDEA -> 帮助 -> 编辑自定义 VM 选项 -> 末尾添加一行 -Dfile.encoding=UTF-8 -> 重启IDEA即可

(注:IDEA 2020 版本已经有官方的汉化插件了)

Spring 的事务一般分为声明式事务(或叫注解事务)和编程式事务,编程式事务比较灵活,可以将事务的粒度控制的更细,并且可以控制何时提交,哪种情况回滚。而声明式事务使用 @Transactional 注解,使用起来没有那么灵活,但是对业务代码没有入侵,而且本身支持一些异常情况下的回滚。但是这个异常是不可以捕获的,如果代码里捕获了,那么事务中已经执行了的部分会提交。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Transactional(rollbackFor = Exception.class)
public boolean update()
{
try
{
// 操作 A
// 操作 B
}
catch (Exception e)
{
log.error("事务失败", e);
return Boolean.FALSE;
}
return Boolean.TRUE;
}

操作 A 成功,B 抛出异常,但是被 catch 捕获并处理,这样异常并没有抛出来,而 Spring 声明式事务是基于 AOP 的,所以也就认为这段操作没有异常,虽然返回的失败,但是 A 操作已经被提交。那么如果想要自己控制异常的时候返回结果,且希望事务回滚要怎么操作呢?其实只要加一段代码就可以,上面的 catch 语句中修改为如下代码:

1
2
3
log.error("事务失败", e);
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return Boolean.FALSE;

加上一段代码,就可以将事务回滚。

注意,sqlserver配置的时候,主机名或ip地址这一栏,要讲ip地址和端口号都要填上,并且,中间用逗号隔开,逗号!\

配置入下图所示:

安装Java环境

1、使用 yum 安装 java8
1
yum search java | grep jdk

这里选择安装 java8

1
yum install java-1.8.0-openjdk
阅读全文 »

前言

最近在做微信的扫描支付的时候,遇到一个问题:如何在用户扫码支付完成之后,客户端立即得到通知,进行下一步的跳转?

解决方案

首先想到的策略是客户端轮循查询订单的状态,根据返回的结果进行跳转

这个方案有明显的缺点,轮循时间设置短,频繁发送请求,对服务器以及数据库都会产生压力;轮循时间过长,用户等待时间长,体验很差;

阅读全文 »

前言

最近在做微信的扫描支付的时候,遇到一个问题:如何在用户扫码支付完成之后,客户端立即得到通知,进行下一步的跳转?

解决方案

首先想到的策略是客户端轮循查询订单的状态,根据返回的结果进行跳转

这个方案有明显的缺点,轮循时间设置短,频繁发送请求,对服务器以及数据库都会产生压力;轮循时间过长,用户等待时间长,体验很差;

阅读全文 »

前言

项目使用到了AES加解密,本地Windows环境测试没有问题,放到生产Linux(Centos7)环境后加解密结果不一致导致程序错误的问题

阅读全文 »