主页

docker

2021-08-31
java

  • 容器container是一个进程,容器运行于属于自己的独立的命名空间,因此容器可以拥有自己的root文件系统、网络配置、进程空间等。容器内的应用进程直接运行于宿主机的内核。
  • 镜像image包含了容器的文件系统,也包含了很多其他的配置。(镜像运行起来就是容器)。docker镜像是分层的结构,镜像构建时会一层层的构建,前一层是后一层的基础,每一层构建完就不会再发生改变,后一层的任何改变只会发生在自己的这一层。如删除前一层的文件,实际上不是真的删除前一层的文件,而是仅在当前层标记该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是该文件实际上会一直跟随镜像。
  • Dockerfile用来创建镜像,docker-compose指定如何运行镜像。
docker ps #获取已运行的容器
docker ps -a #获取所有的容器
docker images #获取所有镜像

#从仓库下载一个镜像
docker pull nginx:latest
#将镜像保存到本地
docker save -o docker-nginx.tar nginx
#从本地导入镜像
docker load -i docker-nginx.tar

#运行一个镜像,-d后台运行。-p将主机的端口映射到容器的端口。
docker run -d -p 80:80 image_name:image_version
#启动一个shell,执行了两个命令,第一个命令随机挑选了一个数并写到了txt文件中,第二个命令让容器保持运行
docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null"
#启动镜像,并执行后面的命令
docker run -it ubuntu ls /
#--name 指定容器的名称
docker run -d -p 17106:3306 --name 171-mysql  mysql8:171-uam

#进入容器中,-i保持stdin打开,-t分配一个终端
docker exec -i -t <container-id> /bin/bash 
#查看容器内此txt文件内容
docker exec <container-id> cat /data.txt

#停止容器运行
docker stop <container-id> 
#移除容器
docker rm <container-id> 
#停止并移除容器运行
docker rm -f <container-id> 

#根据当前路径的dockerfile构建一个镜像。-t给镜像起个名字。
# . 表示在当前路径下,可以使用一个.dockerignore文件来忽略某些文件。
docker build -f Dockerfile -t image_name:image_version .

#创建一个named volume
docker volume create todo-db
#将volumn挂载到/etc/todos路径下
docker run -dp 3000:3000 -v todo_db:/etc/todos image_name:image_version
#查看此named volume的信息
docker volume inspect todo-db 
#使用了一个bind mounts,将主机的当前路径挂载到容器内的/app路径
#-w表示当前的工作路径,-d表示后台运行,-c表示执行后面的命令,-p表示端口映射
docker run -dp 3000:3000  -w /app -v "$(pwd):/app" node:12-alpine sh -c "yarn install && yarn run dev"
#查看logs,-f表示跟随日志输出
docker logs -f <container-id>

#创建了一个network
docker network create todo-app
#启动了一个mysql容器,并接入到network,使用-e定义了初始化数据库用的环境变量,
#mysql还支持MYSQL_HOST(mysql server的主机名),MYSQL_USER。docker自动创建了一个todo-mysql-data的volume。
docker run -d --network todo-app --network-alias mysql \
-v todo-mysql-data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=todos \
mysql:5.7
#输入密码后就能进入到mysql中
docker exec -it <mysql-container-id> mysql -p
#加入到这个网络,并进入到bash
docker run -it --network todo-app image_name:image_version /bin/sh

#扫描一个镜像
docker scan image_name:image_version
#查看创建历史,--no-trunc获取全部输出
docker image history [--no-trunc] mysql:5.7

restart策略 #

  • 当使用docker run命令时可以使用--restart来配置容器的重启策略。可选的标志如下:
flag description
no 任何情况下不会自动重启容器(默认)
on-failure[:max-retries] 当容器由于错误(退出代码为非0)而退出时会重启容器。使用:max-retries来限制容器尝试重启的次数。
always 始终在容器停止时重启,除非容器是被手动停止的。在docker重新启动时容器也会重启。
unless-stopped 类似于always,但是当容器停止后,重启docker容器并不会启动。

参考链接: use-a-restart-policy

docker-compose #

  • docker-compose version获取版本信息。
  • 创建一个叫docker-compose.yml的文件,如将下面这个命令转化为docker-compose。
#转换前
docker run -dp 3000:3000 -w /app -v "$(pwd):/app" --network todo-app \
  -e MYSQL_HOST=mysql -e MYSQL_USER=root \
  -e MYSQL_PASSWORD=secret -e MYSQL_DB=todos \
  node:12-alpine  sh -c "yarn install && yarn run dev"

#转换前
docker run -d \
  --network todo-app --network-alias mysql \
  -v todo-mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret -e MYSQL_DATABASE=todos \
  mysql:5.7
#转换后
version: "3.7"

services:
  app:
    image: node:12-alpine
    ports:
      - 3000:3000
    working_dir: /app
    #docker compose中volume定义可以使用相对路径。
    volumes:
      - ./:/app
    command: sh -c "yarn install && yarn run dev"
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos
  
  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos
      
volumes:
  todo-mysql-data:
  • 使用docker-compose up -d命令执行,-d参数使得在后台运行,这个命令会自动在应用间创建network。
  • 使用docker-compose logs -f来查看日志。-f表示follow,会将日志的变化输出到控制台。
    • 使用docker-compose logs -f app来查看特定服务的日志。
  • docker-compose down [--volumes]关闭并移除,–volumes指定是否删除volumes。

dockerfile #

  • 一个Dockerfile大部分情况下以FROM指令开始,FROM指令指定了构建的父镜像。
  • ENV用来设置环境变量,通过${xxx}的方式使用。
  • CMD在docker run时运行,RUN在docker build时运行。
  • RUN会在新的一层执行命令。

多阶段构建 #

FROM maven AS build
WORKDIR /app
COPY . .
RUN mvn package

FROM tomcat
COPY --from=build /app/target/file.war /usr/local/tomcat/webapps 

参考链接: Orientation and setup | Docker Documentation

gradle

2021-08-30
java

settings.gradle #
rootProject.name = 'demo'
include('app')
  • rootProject指定了构建的名称,默认以上级目录命名。
  • include(’…’)定义了一个叫app的子项目,子项目包含自己的代码和构建逻辑。
gradle.properties #

最终的配置组合了所有的命令行提供的属性和gradle.properties文件。

配置优先级: #
  1. 命令行使用-P/–project-prop传递的参数;
  2. GRADLE_USER_HOME目录的gradle.properties;
  3. 项目根目录的gradle.properties;
  4. gradle安装目录的gradle.properties.
#指定构建时jvm参数。
org.gradle.jvmargs=-Xms2g
#并行编译,会使用org.gradle.workers.max参数
org.gradle.parallel=(true,false)
#默认为CPU的数量
org.gradle.workers.max=

#使用systemProp.前缀可在gradle.properties中配置系统属性,或者命令行中使用-D(不加systemProp,有多个project时,只有根路径下的以systemProp开头的属性会被使用,其他的都会被忽略。

参考链接: Build Environment (gradle.org)

构建 #

tasks #

  • 每个gradle build由多个projects组成,一个project又由多个tasks组成。
tasks.register('upper') {
    doLast {
        String someString = 'mY_nAmE'
        println "Original: $someString"
        println "Upper case: ${someString.toUpperCase()}"
    }
}
tasks.register('hello') {
    dependsOn tasks.upper
    doLast {
        print 'hello world'
    }
}

#执行gradle -q hello-q表示抑制日志消息


tasks.register('hello') {
    doLast {
        println 'Hello Earth'
    }
}
tasks.named('hello') {
    doFirst {
        println 'Hello Venus'
    }
}
tasks.named('hello') {
    doLast {
        println 'Hello Mars'
    }
}
tasks.named('hello') {
    doLast {
        println 'Hello Jupiter'
    }
}

#执行gradle -q hello
#doFirstdoLast可以被执行多次,他们被添加到taskactions list的开始或结束位置,当task执行时,在action list中的action会被按顺序执行。

默认task #

  • gradle允许定义多个默认的task
defaultTasks 'clean', 'run'

tasks.register('clean') {
    doLast {
        println 'Default Cleaning!'
    }
}

tasks.register('run') {
    doLast {
        println 'Default Running!'
    }
}
#通过gradle -q执行

为构建脚本添加外部依赖 #

  • 如果构建脚本需要使用外部的库,可以使用buildscript,buildscript中添加的依赖只对构建脚本有效。
//使用库中的某个类
import org.apache.commons.codec.binary.Base64

buildscript {
    repositories {
        mavenCentral()
    }
    //将库添加到classpath路径下
    dependencies {
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
    }
}

tasks.register('encode') {
    doLast {
        def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
        println new String(encodedString)
    }
}

#gradle -q encode

slf4j

2021-08-30
java

SLF4J:the simple logging facade for java。

  • 作为各种日志框架的门面。

计算机网络

2021-08-27
计算机网络

  • Internet是全球最大的互联网络,是一个世界范围的计算机网络,互联了遍及全世界的数以亿计的计算设备的网络。
  • 主机(host):也叫端系统。是与因特网相连的计算机和其他设备,如PC、服务器、手机等。主机通过通信链路(电缆、光纤、无线电等)和分组交换机(路由器、链路层交换机)连接到一起。当一台主机向另一台主机发送数据,发送主机将数据分组(将数据分段,每段加上首部字节)。分组交换机从他的一条入通信链路接收到达的分组,从它的出通信链路转发该分组。路由器通常位于网络核心,链路层交换机位于接入网中。
  • ISP(Internet Service Provider):因特网服务提供商。主机通过ISP介入到因特网,每个交换机由多个通信链路和分组交换机组成。
  • 协议:计算机网络中数据交换必须遵守事先约定的规则。协议规定了网络中所有信息发送和接收过程。协议的三要素:
    1. 语法,数据和控制信息的结构或格式。
    2. 语义,需要发出何种控制信息;完成何种动作及做出何种响应;以及差错控制。
    3. 时许,事件的顺序;速度匹配。
  • 报文指的是发送信息的整体,比如一个文件、一首音乐。
  • 分组是报文分拆出来的一系列相对较小的数据包。
  • 接入网络:①DSL(Digital Subscriber Line),即数字电话线;②电缆网络;③无线局域网和广域无线接入。
  • 计算机网络结构:
    • 网络边缘,指主机和网络应用。
    • 接入网络,有线和无线通信链路。
    • 网络核心(核心网络),互联的路由器。网络核心的关键功能是路由和转发,路由是确定分组从源到目的的传输路径,转发是将分组从路由器的输入端交换至正确的输出端口。

数据交换 #

  • 网络核心需要通过数据交换将数据从源主机发送到目的主机,数据交换有以下类型:1. 电路交换。2. 报文交换。3. 分组交换。
  • 电路交换的三个阶段是:1. 建立通信;2. 通信;3. 释放连接。会独占资源。为了共享中继线,引入了多路复用(Multiplexing)。典型的多路复用有:1. 频分多路复用(FDM);2. 时分多路复用(TDM);3. 波分多路复用(WDM);4. 码分多路复用(CDM)。
  • 频分多路复用是各用户占用不同的频率带宽。用户在分派到一定的频带后,在通信过程中始终占用此频带。
  • FDM
  • 时分复用则是将时间划分为一段段等长的时分复用帧(TDM 帧),每个用户在每个TDM帧中占用固定序号的时隙。时分复用的所有用户是在不同的时间占用相同的频带宽度。
  • TDM
  • 波分复用就是光的频分复用。
  • WDM
  • 码分多路复用是为每个用户分配一个唯一的mbit码片序列,各用户码片序列相互正交。
  • 报文交换和分组交换均采用存储转发的方式交换数据,报文交换以完整的报文进行存储转发,分组交换以较小的分组进行存储转发。
  • 分组交换的报文的交付时间:
  • FDM
  • 分组交换和电路交换的比较:
    1. 分组交换适合突发数据传输网络。
    2. 电路交换会独占资源,分组交换不会,所以分组交换可能产生拥堵和分组丢失。

网络性能指标 #

  1. 速率或比特率,使用单位时间中传输信息的比特量。1Gbps=10^3Mbps=10^6kbps=10^9bps
  2. 带宽指的是数字信道所能传输的最高数据率。单位是bps
  3. 分组交换可能会发生丢包和时延。因为分组在路由器缓存中排队时,如果分组到达速率超过输出链路容量时就会丢包;分组排队等待输出链路可用时就会产生时延,时延包括节点处理延迟(通常小于毫秒级别)、数据报排队延迟、路由器中传输延迟和物理链路传播延迟。
  4. 时延带宽积又被称为以比特为单位的链路长度,时延带宽积=传播时延x带宽。
  5. 丢包率=丢包数/已发分组数。
  6. 吞吐量标识在发送端和接收端之间传送数据速率。吞吐量取决于链路上最小的带宽。

OSI参考模型和TCP/IP参考模型 #

  • 现实中基本上都用的TCP/IP参考模型。
  • 每一层都会封装上层的数据,增加一些控制信息用来构建协议数据单元(PDU)。
  • OSI参考模型
    • 物理层定义了接口特性、传输模式、比特编码、比特同步和数据率。
    • 数据链路层是相邻网络元素(主机、交换机、路由器等)的数据传输,负责节点之间的数据传输、组帧、物理寻址、流量控制、差错控制和访问控制(决定某一时刻哪个设备拥有物理介质的使用权)。
    • 网络层负责源主机到目的主机的数据分组路由与转发以及逻辑寻址。
    • 传输层负责进程间完整报文的传输。包括分段与重组、SAP寻址、连接控制、流量控制、差错控制。
    • 会话层负责对话的建立、维护和同步。
    • 表示层负责数据的加密解密、压缩解压等。
    • 应用层支持用户通过用户代理(如浏览器)或网络接口使用网络。

对称密钥

2021-08-25
信息安全

用相同的密钥进行加密和解密

  • 异或(XOR)(exclusive or)⊕
  • 如果将01001100记为A,10101010记为B,A于B的xor运算结果为(不进位)11100110。将结果在xorB,又得到了A。只选择一个合适的B,仅仅使用XOR就能得到一个高强度的密文。

密码学起步

2021-08-25
信息安全

  • 信息安全面临的威胁:

    • 机密性:秘密被泄露
    • 完整性:信息被篡改
    • 认证:伪装成发送者
    • 不可否认性:事后否认自己没做
  • 密码的常识:

    • 不要使用保密的密码算法
    • 使用低强度的密码比不进行任何加密更危险
    • 任何密码总有一天都会被破解
    • 密码只是信息安全的一部分
  • 报文的最初形式称为明文

  • 使用加密算法加密明文,生成的加密报文称为密文,加密算法往往是已知的。

  • 密钥KA是一串数字或者字符,加密算法以密钥和明文报文m为输入,生成的密文为输出。用KA(m)表示。

  • 解密算法使用密钥KB和密文作为输入,通过计算KB(KA(m))得到m。

  • 对称密码(symmetric cryptography)是指在加密和解密时使用同一密钥的方式。

  • 公钥密码(pulbic-key cryptography)是指在加密和解密时使用不同密钥的方式。又称为非对称密码(asymmetric cryptography)。

  • 混合密码系统(hybrid cryptosystem)是将对称密钥和公钥密码结合起来的密码方式。

  • 单向散列函数(one-way hash function)是一种保证完整性的密码技术。

  • 消息认证码(message authentication code)是一种能够保证消息完整性和提供认证的密码技术。

  • 数字签名(digital signature)是一种能确保完整性、提供认证并防止否认的密码技术。

  • 伪随机数生成器(Pseudo Random Number Generator,PRNG)是一种能够模拟产生随机数列的算法,随机数承担着密钥生成的重要职责,在web中进行SSL/TLS通信时,会生成一个仅用于当前通信的临时密钥,这个密钥是基于伪随机数器生成的。

凯撒密码 #

  • 将明文报文中的每个字母用字母表中该字母后第k个字母进行替换,允许回绕。
  • 密钥空间(keyspace)是指能够使用的“所有密钥的集合”,所有可以使用的密钥的总数越大,密钥空间越大,凯撒密码的密钥空间的大小是25。
  • 很容易进行暴力破解。

简单替换密码 #

  • 将字母表中的26个字母,分别将这26个字母本身建立一对一对应关系(替换表)。加密过程是依次将明文中的每一个字母按照替换表替换成另一个字母。
  • 很难通过暴力破解,因为它的密钥总数是26x25x24…x2x1。
  • 通过频率分析,就能破解。其利用明文中的字母出现的频率和密文中的字母出现的频率一致的特性。

logback

2021-08-23
java

例: #

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>testFile.log</file>
<!--         将 immediateFlush 设置为 false 可以获得更高的日志吞吐量 -->
        <immediateFlush>false</immediateFlush>
<!--         默认为 ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
        </encoder>
    </appender>
    
    <appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
        <syslogHost>remote_home</syslogHost>
        <port>5140</port>
        <facility>AUTH</facility>
        <suffixPattern>[%thread] %logger %msg</suffixPattern>
    </appender>
    
    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
    <logger name="space.xiaoxiang" level="debug" additivity="true"/>
</configuration>

appender #

AppenderBase #

  • 是一个抽象类,实现了Appender接口,提供了基本方法供appender使用。

OutputStreamAppender #

  • 是ConsoleAppender、FileAppender以及 RollingFileAppender的父类。

ConsoleAppender #

  • 将日志输出到控制台,ConsoleAppender通过用户指定的encoder格式化日志事件。

FileAppender #

  • 将日志输出到文件中,通过file来指定目标文件。

RollingFileAppender #

  • 具有轮转日志文件的功能。比如在满足特定的条件后,将日志输出到另一个文件。

SocketAppender 和 SSLSocketAppender #

  • 可以将日志传输到远端机器。

SMTPAppender #

  • 收集日志到缓冲区中,当用户指定的事件发生时,将从缓冲区中取出适当的内容进行发送。

DBAppender #

  • 将日志插到三张数据库表中,三张表分别是logging_event, logging_event_property 与 logging_event_exception。

SyslogAppender #

  • 可以将日志发送给远程的syslog守护线程。
  • 日志事件的严重程度是根据日志事件的级别转换来的,DEBUG被转换为7,INFO被转换为6,WARN为4,ERROR为3。

SiftingAppender #

  • 根据给定的运行时属性分离或者过滤日志。

AsyncAppender #

  • 作为一个事件调度器存在,必须调用其他appender来完成操作。

encoder #

  • encoder将日志事件转换为字节数组,同时将字节数组写入到一个OutputStream中。
  • PatternLayoutEncoder是目前唯一真正有用的encoder,它仅包裹了一个PatternLayout就完成了大部分的工作。

layout #

  • layout负责将日志事件转化为字符串。

PatternLayout #

  • 可以通过调整PatternLayout的转换模式来进行定制,PatternLayout的转换模式由字面量和转换说明符组成,每一个转换说明符由一个百分号开始%,后面跟随格式修改器,以及可用大括号括起来的转换字符和可选的参数。格式修改器可以对字段进行对齐,修改最大最小宽度等。

  • 括号用于对转换模式进行分组,()都有特殊的含义。

  • 名为com.foo的logger是名为com.foo.bar的logger的父级。(命名层次结构)

  • root logger的默认层级为DEBUG。

  • 层级的排序:TRACE < DEBUG < INFO < WARN < ERROR。

  • appender具有叠加性:logger L的日志输出会遍历L和它父级中所有的appender,如果L的某个上级P,P设置了additivity=false,那么L的日志输出会遍历从L到P(不包括P)的所有appender。additivity默认为true。

logback初始化步骤 #

  1. 在类路径下寻找名为logback-test.xml。
  2. 如果没找到,找名为logback.groovy的文件。
  3. 如果没找到,找名为logback.xml的文件。
  4. 如果没找到,会用ServiceLoader工具去解析META-INF\services\ch.qos.logback.classic.spi.Configutator路径下实现了Configurator接口的类
  5. 如果还没找到,logback会通过BasicConfigurator为自己进行配置,日志将会全部打印在控制台。

oAuth

2021-08-20
java

  • oAuth是一种授权机制,数据所有者告诉授权系统,允许第三方应用获取这些数据,授权系统从而产生一个短期的进入令牌(token),第三方应用通过令牌获取数据。

授权码方式 #

第三方应用(记为a)访问CALLBACK_URL这个网站,会重定向到授权系统(记为b),url如1所示

1. 请求授权码 #

https://b.com/oauth/authorize?
  response_type=code& #表示参数要求返回授权码
  client_id=CLIENT_ID& #客户端idb就知道是谁在请求
  redirect_uri=CALLBACK_URL& #b接收请求后跳转的网站
  scope=read #授权范围

2. 返回授权码 #

在授权系统登录成功后,会重定向到CALLBACK_URL?code=AUTHORIZATION_CODE,code就是授权码。

3. 请求令牌 #

CALLBACK_URL这个网站会重定向到授权系统,url如下:

https://b.com/oauth/token?
 client_id=CLIENT_ID& #客户端idb就知道是谁在请求
 client_secret=CLIENT_SECRET& 
 grant_type=authorization_code& #授权类型,表明是授权码
 code=AUTHORIZATION_CODE& #上一步拿到的授权码
 redirect_uri=CALLBACK_URL #b之后跳转的网站

4. 返回令牌 #

授权系统会重定向到CALLBACK_URL,并发送一段JSON数据

{  
  "access_token":"ACCESS_TOKEN(令牌)",  
  "token_type":"bearer",
  "expires_in":2592000,
  "refresh_token":"REFRESH_TOKEN",
  "scope":"read",
  "uid":100101,
  "info":{...}
}

参考链接: OAuth 2.0 的四种方式 - 阮一峰的网络日志 (ruanyifeng.com)