0%

PostStart

容器生命周期钩子(Container Lifecycle Hooks)监听容器生命周期的特定事件,并在事件发生时

spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/usr/sbin/nginx","-s","quit"]

执行已注册的回调函数。支持两种钩子:

  • postStart: 容器启动后执行,注意由于是异步执行,它无法保证一定在ENTRYPOINT之后运行。如果失败,容器会被杀死,并根据RestartPolicy决定是否重启
  • preStop:容器停止前执行,常用于资源清理。如果失败,容器同样也会被杀死

而钩子的回调函数支持两种方式:

  • exec:在容器内执行命令
  • httpGet:向指定URL发起GET请求

关于postStart异步执行测试

apiVersion: v1
kind: Pod
metadata:
  name: test-post-start
spec:
  containers:
  - name: test-post-start-container
    image: busybox
    command: ["/bin/sh", "-c", "sleep 5 && echo $(date) 'written by entrypoint' >> log.log && sleep 600"]
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "sleep 10 && echo $(date) 'written by post start' >> log.log"]

创建上面的pod,通过进入pod,查看log.log打印日志,证明:

  • PostStart是否会挡住主进程的启动
  • PostStart是否是异步执行

如果 PostStart 会阻挡 ENTRYPOINT 的启动,则日志文件内容应该是:

(时间点 T)written by post start
(时间点 T + 约 10 秒)written by entrypoint

否则内容应该是:

(时间点 T)written by entrypoint
(时间点 T + 约 5 秒)written by post start
```log

实验结果:
```log
/ # cat log.log
Thu Jun 4 06:14:50 UTC 2020 written by entrypoint
Thu Jun 4 06:14:55 UTC 2020 written by post start

修改YML

apiVersion: v1
kind: Pod
metadata:
  name: test-post-start
spec:
  containers:
  - name: test-post-start-container
    image: busybox
    command: ["/bin/sh", "-c", "sleep 15 && echo $(date) 'written by entrypoint' >> log.log && sleep 600"]
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "sleep 10 && echo $(date) 'written by post start' >> log.log"]

如果 PostStart 不是异步执行,则日志文件内容应该是:

(时间点 T)written by entrypoint
(时间点 T + 约 5 秒)written by post start
```log

否则内容应该是:
```log
(时间点 T)written by post start
(时间点 T + 约 5 秒)written by entrypoint

实验结果:

[root@master k8s]# kubectl exec -it test-post-start sh
/ # cat log.log
Thu Jun 4 06:17:54 UTC 2020 written by post start
Thu Jun 4 06:17:59 UTC 2020 written by entrypoint
/ # 

实验结论

  • PostStart不会挡住主进程的启动
  • PostStart是异步执行

正反代理

正向代理代理的是客户端,如国内访问国外网站,有防火墙限制,我们可以通过VPN去访问;

反向代理代理的是服务器,如机房内多个后端服务,通过Nginx对外提供服务;

Nginx

  • 默认配置文件nginx.conf

nginx单点问题

可以结合keepalived解决单点问题

实现ElementHandler接口

public class MyHandler implements ElementHandler {

    // xml中每一个<节点>开始执行的方法
    @Override
    public void onStart(ElementPath elementPath) {
        // todo 解析xml要做的业务处理
    }

    // xml中每一个<节点>结束执行的方法
    @Override
    public void onEnd(ElementPath elementPath) {
        // todo 解析xml要做的业务处理
        elementPath.getCurrent().detach();
    }
}

ElementPath获取一些信息

// 获取当前解析的<节点>是第几级,根节点为第1级
public static int getLevel(ElementPath path) {
    return path.getPath().split("/").length - 1;
}
// 获取当前节点的节点名
public static String getElementName(Element element) {
    return element.getName();
}

// 获取当前节点属性
public static String getAttribute(Element element) {
    return element.attribute("attribute-name");
}

// 获取当前节点文本
public static String getText(Element element) {
    return element.getText();
}

解析示例

<!-- 要解析的xml格式 -->
<root>
    <book id="1">
        <name>菜鸟编程</name>
        <describe>针对菜鸟编程学习</describe>
    </book>
    <book id="2">
        <name>老鸟编程</name>
        <describe>针对老鸟编程学习</describe>
    </book>
</root>
// book映射对象
@Data
public class Book {
    private int id;
    private String name;
    private String describe;
}
// 解析book.xml伪代码
public class BookHandler implements ElementHandler {
    // 创建一个Book对象
    private Book book;
    // xml中每一个<节点>开始执行的方法
    @Override
    public void onStart(ElementPath elementPath) {
        // 获取节点级数,<root>为1,<book>为2,<name><describe>为3
        level = getLevel(elementPath)
        switch(level):
        case 1: // 不做处理
        case 2: // 每次读取到<book id="xx">这一行会执行
                // 创建book对象,将读取的id属性写入
                book = new Book();
                book.setId();
        case 3: // 每次读取到<book>的子节点会执行
                // 无需处理
    }

    // xml中每一个<节点>结束执行的方法
    @Override
    public void onEnd(ElementPath elementPath) {
        // 获取节点级数,<root>为1,<book>为2,<name><describe>为3
        level = getLevel(elementPath)
        switch(level):
        case 1: // 不做处理
        case 2: // 每次读取到</book>这一行会执行
                // 说明这一本书所有信息已经读完,需要将book对象保存,或写入数据库,然后清空
                saveDB(book); // 保存数据方法
                book = null;
        case 3: // 每次读完一个<book>的子节点会执行
                // 可以读取book的name、describe属性,并写入到book对象中
                // 注意 一次只能获取到一个属性,要做一些处理
                String name = getAttribute("name");
                String describe = getAttribute("describe");
                book.setName(name);
                book.setDescribe(describe);
        elementPath.getCurrent().detach();
    }
}
// 读取xml,使用BookHandler解析
BookHandler bookHandler = new BookHandler();
SAXReader reader = new SAXReader();
reader.setDefaultHandler(bookHandler);
reader.read(new File(xmlPath));

dom4j

DOM4J是一个开源XML解析包。DOM4J应用于Java平台,采用了Java集合框架并完全支持DOMSAXxpathJAXP

dom4j接口说明

接口 说明
Attribute 属性
Branch 分支,指能够包含子节点的节点。如Element,(Docuemnts)
CDATA XML CDATA区域
CharacterData 是一个标识接口,标识基于字符的节点。如CDATAComment, Text
Comment 注释
Document 文档
DocumentType XML DOCTYPE 声明
Element 定义XML元素
ElementHandler Element对象的处理器
ElementPath ElementHandler使用,用于取得当前正在处理的路径层次信息
Entity 定义XML entity
Node 节点
NodeFilter 节点过滤器
ProcessingInstruction 定义XML处理指令
Text 文本节点
Visitor 用于实现Visitor模式
XPath XPath表达式

dom4j依赖

<!-- https://mvnrepository.com/artifact/dom4j/dom4j -->
<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>
<!--dom4j使用xpath依赖的包-->
<dependency>
    <groupId>jaxen</groupId>
    <artifactId>jaxen</artifactId>
    <version>1.1.6</version>
</dependency>

文档操作

SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new File(filename));  //读取XML文件,获得document对象
// Document document = saxReader.read(url);

diff命令说明

Linux diff命令用于比较文件的差异。

diff以逐行的方式,比较文本文件的异同处。如果指定要比较目录,则diff会比较目录中相同文件名的文件,但不会比较其中子目录。

使用

diff <文件1> <文件2> [附加参数]

示例

diff test1.txt test2.txt

常用参数

  • -b--ignore-space-change  不检查空格字符的不同
  • -B--ignore-blank-lines  不检查空白行
  • -c  显示全部内文,并标出不同之处
  • -H--speed-large-files 比较大文件时,可加快速度
  • -i--ignore-case  不检查大小写的不同
  • -y--side-by-side  以并列的方式显示文件的异同之处
  • -W<宽度>或--width<宽度>  在使用-y参数时,指定栏宽

结果说明

  • |表示前后2个文件内容有不同
  • <表示后面文件比前面文件少了1行内容
  • >表示后面文件比前面文件多了1行内容

使用MyBatis查询 返回类型为int,但是当查询结果为空NULL,报异常的解决方法

select pre_id from task_child where parent_id=3 order by pre_id desc limit 1;

如果查询结果是空,那么后台可能报异常

Servlet.service() for servlet [springDispatcherServlet] in context with path [/xxxx] threw exception [Request processing failed; 
nested exception is org.apache.ibatis.binding.BindingException: Mapper method 'com.xxx.PersonRoleRelationMapper.getPersonRecordno attempted to return null from a method with a primitive return type (int).] 
with root cause org.apache.ibatis.binding.BindingException: Mapper method 'com.xxx.PPersonRoleRelationMapper.getPersonRecordno attempted to return null from a method with a primitive return type (int).

这时,我们需要给查询结果一个默认值

select IFNULL(MAX(pre_id),-1) from task_child where parent_id=3 order by pre_id desc limit 1;

或者

-- 在Oracle中我们可以这样写:
select NVL(max(pre_id),0) ...

--对于所有数据库适用的方法可以这样写:
select COALESCE(max(pre_id),0) ...

max也可以用sum函数代替.