主页

nginx

2021-08-12
nginx

  • nginx默认的配置文件名是nginx.conf,位置在/usr/local/nginx/conf/etc/niginx、或/usr/local/etc/nginx。通过nginx -c xxx.conf来指定配置文件。

  • 当nginx启动后,可以通过nginx -s signal来控制nginx,signal可以是:

    • stop:快速关闭;quit:正常关闭;reload:重新加载配置文件;reopen:重新打开配置文件。
  • 配置文件可以包含多个server块,他们之间通过监听的端口和服务名来区分。一旦nginx决定使用哪个服server来处理请求,他就会根据server块内location的指令来匹配请求头中的url,①精确匹配优先级最高,遇到就返回结果;②普通匹配会选择location中前缀最长的那个,和顺序无关;③当location中有正则表达式时,会优先匹配正则表达式(正则级别比普通匹配优先级高,但比精确匹配优先级低),正则表达式的匹配顺序按照文件中的物理顺序匹配,只要匹配到一条正则,就会返回结果;如果没有匹配,就会取普通匹配中最匹配的那个。

  • nginx的错误日志文件在usr/local/nginx/logs/var/log/nginx

常用配置 #

proxy_pass #

  • 使用proxy_pass来配置代理服务。代理服务的流程:server接收request->把request传给代理服务->获取代理服务的response->把response返回给客户端。

例: #

server {
	listen 80;
    #不带 / 结尾:保留 location 匹配的路径,追加到代理地址后
	location /api/ {
		proxy_pass http://backend;  # 请求 /api/test → http://backend/api/test
	}
    
    #带 / 结尾:proxy_pass 的 URL 若以 / 结尾,Nginx 会替换 location 匹配的部分。 
	location /api/ {
		proxy_pass http://backend/;  # 请求 /api/test → http://backend/test
	}

    #将会映射到/html/tool这个路径下
    location /tool {
    	root html;
    }
    #正则表达式以~开始,这里是匹配图片
    location ~ \.(gif|jpg|png)$ {
        root /data/images;
    }
}

参考链接: Beginner’s Guide (nginx.org)

nginx配置代理yum源和apt源 #

  1. nginx配置文件中加入如下配置,启动nginx
	server {
		listen 8803;
        #必须要有DNS解析的配置
		resolver 114.114.114.114;
		
		location / {
			proxy_pass http://$http_host$request_uri;
		}
    }
  1. ubuntu中创建/etc/apt/apt.conf文件,填入如下内容/etc/apt/apt.conf 随后执行apt update即可完成代理。
Acquire::http::Proxy "http://131.110.99.99:8803/";
  1. centos或者rocky中,编辑文件/etc/yum.conf,加入proxy=http://131.110.99.99:8803(完整文件如下所示),同时还需要将源配置文件中的https改成httpsed -i 's/https/http/g' /etc/yum.repos.d/*.repo,随后执行yum makecache即可。
[main]
proxy=http://131.110.99.99:8803
gpgcheck=1
installonly_limit=3
clean_requirements_on_remove=True
best=True
skip_if_unavailable=False

nginx内嵌的变量 #

#等于在proxy_pass指令中指定的被代理服务的主机名和端口
$proxy_host
#等于在proxy_pass中指定的服务的端口,或者是其服务的默认端口
$proxy_port
#如果X-Forwarded-For属性未在请求头中,$proxy_add_x_forwarded_for的值就等于$remote_addr;
#如果X-Forwarded-For在请求头中,那$proxy_add_x_forwarded_for的值就等于上一个$proxy_add_x_forwarded_for加上",$remote_addr"。
#例:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for
$proxy_add_x_forwarded_for

nginx指令 #

rewrite #

语法: rewrite regex replacement [flag];
context: server,location,if
rewrite指令按照它出现的顺序来执行,可以通过flag来终止执行,如果replacement以"http://,https://或者$scheme"开始,就会直接返回客户端。
flag可以是last、break、redirect、permanent。
redirect是暂时重定向302,permanent是永久重定向301。

rewrite ^(.*)$ https://localhost$1 permanent;

if #

语法: if(condition) {...}
context: server, localtion
condition包括:
  1. 变量名,当变量为空或者为"0"时是false;
  2. 变量和字符串通过"="或者"!="来比较;
  3. 变量和正则表达式比较【比较符号: ~(大小写敏感)、~*(大小写)不敏感、!~、!~*】,可以通过()来捕获数据。
  4. 使用-f 或!-f检测文件书否存在;
  5. 使用-d 或!-d检测目录是否存在;
  6. 使用-e 或!-e 检测文件、目录、符号连接是否存在;
  7. 使用-x 或!-x 检测是否是可执行文件。
  
if ($http_user_agent ~ MSIE) {
    rewrite ^(.*)$ /msie/$1 break;
}

if ($http_cookie ~* "id=([^;]+)(?:;|$)") {
    set $id $1;
}

if ($request_method = POST) {
    return 405;
}

return #

语法: return code [text];
     return code URL;
     return URL;
context: server,location,if
返回特定的状态码,返回444会不发送响应头就关闭连接。
可以指定一个重定向的URL(301,302,303,307,308),或者响应体text(其他的code)。

set #

语法: set $variable value;
context: server,location,if
给变量赋值,值可以是变量、字符串或者变量和字符串的组合。

网络安全

2021-08-01
计算机网络, 信息安全

安全通信需要具有下列的特性

  • 机密性:仅有发送方和希望的接收方能够理解传输报文的内容。

  • 报文完整性:确保其通信内容在传输的过程中未被改变。

  • 端点鉴别:发送方和接收方都能证实通信过程所涉及的另一方。

  • 运行安全性:确保网络安全。

密码学 #

报文的最初形式称为明文

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

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

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

对称密钥密码体制 #

  1. 凯撒密码
    • 将明文报文中的每个字母用字母表中该字母后第k个字母进行替换,允许回绕。
    • 缺点:密钥值只有25个
  2. 单码代替密码
    • 使用字母表中的一个字母替换另一个字母。需要给定一个替换表,每个字母都有唯一一个的替换字母。
    • 缺点:由于是定向替换,当对密文进行统计分析,并且常见的两三个字母的组合(in、on、the)往往是一起出现,而且如果入侵者知道该密文具有某些可能的东西,破解该密文就不那么困难了。
  3. 多码代替密码
    • 使用多个单码来替换明文的字母,即从多个字母中选一个代替明文中的一个字母,如何选怎呢?需要指定一个次序,比如第一次选择多个字母的第一个,第二次选择多个字母的第二个,第三次选择多个字母的第二个(122),然后按照这个顺序循环多次,直到完成加密。

块密码:要加密的报文被处理为k比特的块。例如。如果k=64,字报文被划分为多个64比特的块,每块被独立加密。

密码块链接:使用块密码时,如果有多个明文块可能是相同的,将会产生相同的密文,解决方式是:发送方为第i个块生成一个随机的k比特数,通过将该数与第i个明文块异或,再将结果加密。该方法有一个问题,就是传输的时候需要两倍的带宽,块密码使用了一种称为密码块链接(Cipher Block Chaining,CBC)的技术,基本思想是只传输一个随机值,剩余的让发送方和接收方计算。流程如下:

  1. 发送方生成一个随机的k比特串,称为初始向量c(0),发送方以明文方式发送。

  2. 对于第一个块,发送方计算m(1)⊕c(0),然后加密得到c(1)=Ks(m(1)⊕c(0))。发送方向接收方发送加密块c(1)

  3. 对于第i个块,发送方根据c(i)=Ks(m(1)⊕c(i-1))向接收方发送第i个密文块。

公开密钥加密 #

世界上任何人都都可以得到公钥(public key)KB+

只有主人知道的私钥(private key)KB-

假如A要和B通信,A首先要取得B的公钥,然后用这个公钥和一个众所周知的加密算法加密他要传给B的报文m,即KB+(m),B接到A的加密报文后,用其私钥和一个众所周知的解密算法解密报文,即计算KB-(KB+(m)),这样就得到了最初的明文m。此外还有KB-(KB+(m)) = KB+(KB-(m))=m。

  1. RSA
    • 生成RSA的公钥和私钥
      1. 选择两个大素数p和q,p和q越大,破解RSA越困难,执行加密和解密所用的时间也越长。推荐p和q的乘积为1024比特的数量级
      2. 计算n=pq,z=(p-1)(q-1)
      3. 选择一个小于n的数e,使得e和z没有公因数,e用来加密
      4. 求得一个数d使得ed-1可以被z整除,d将用来解密(ed mod z = 1)
      5. 外界可用的公钥KB+是一对数(n,e),私钥KB-是一对数(n,d)
    • 加密和解密过程
      1. A给B发送一个由整数m表示的比特组合,m<n。加密后的值c=me mod n,将密文c发送给B
      2. B通过计算获得m,m=cd mod n
  2. 会话密钥:RSA的指数运算极其耗时,在实际应用中,RSA通常与对称密钥结合使用。首先,A选择一个用于加密数据本身的密钥,这个密钥有时候称为会话密钥Ks。A通过公钥将Ks传输给B,B通过私钥解密后,就获取了Ks,然后使用Ks来传输数据。

报文完整性和数字签名 #

密码散列函数 #

密码散列函数的性质:找到任意两个不同的报文x和y使得H(x)=H(y),在计算上是不可能的。

算法:MD5(128位)、SHA-1(160位)

为了执行报文完整性,A和B需要共享秘密s,这个共享的秘密是个比特串,被称为鉴别密钥。使用这个共享秘密,报文完整性能够执行如下:

  1. A生成报文m,用s级联m生成m+s,并计算散列H(m+s)。H(m+s)被称为报文鉴别码(Message Authentication Code,MAC)。
  2. A将MAC附加到报文m上,生成扩展报文(m,H(m+s)),将该扩展报文发送给B。
  3. B收到扩展报文(m,h),由于知道s,计算出报文鉴别码H(m+s),如果其等于h,则证明一切正常。

数字签名 #

数字签名是B先使用散列函数对报文生成散列,使用私钥对散列进行加密(数字签名)。

A获取到了B的明文报文和已经数字签名的报文摘要,先使用B的公钥解密数字签名,得到一个散列结果,再使用散列函数获取报文的散列,如果这两个散列匹配,则可以确信报文的完整性和发送方。

公钥认证 #

证实一个公钥属于某个特定的实体。

将公钥与特定的实体绑定通常是由认证中心(Certification Authority,CA)完成的。CA证实了一个实体的真实身份;一旦CA验证了某个实体的身份,这个CA会生成一个将其身份和实体的公钥绑定起来的证书。

使TCP连接安全:SSL #

SSL:Secure Socket Layer,安全套接口协议

TLS:Transport Layer Security,TLS 运输层安全性,是SSL版本3的一个稍加修改的版本

equals

2021-04-26
java

Object #

  • Object的equals方法
    public boolean equals(Object obj) {
        return (this == obj);
    }
  • Object的toString方法
	public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

String #

  • String的equals方法
    public boolean equals(Object anObject) {
        //如果指向的是同一个对象就返回true
        if (this == anObject) {
            return true;
        }
        //如果该对象是String的实例
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            //value存放的是String本身的字符串
            int n = value.length;
            //如果两个字符串的长度相等
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                //按下标比较两个字符串的字符
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
  • String的hashCode方法
	private int hash; // Default to 0	
	//...
	//当通过new String("xxx")方式创建的字符串,原hash就被赋值给了现在的hash
    public String(String original) {
        this.value = original.value;
        this.hash = original.hash;
    }
	//当hash为0并且字符串长度不为0时,现在的hash是通过循环计算出来的
	//由该计算可知道,当字符串一样,它的hashcode就一样
	public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

maven-dependency-scope

2021-04-23
java

共6个作用域 #

  1. compile

    • 默认作用域,在项目的编译、运行、测试中都有效,依赖传递
  2. provided

    • 在项目编译、测试中有效,运行中无效;依赖不传递,适用于一些web容器提供了运行时的依赖的情况。
  3. runtime

    • 只在项目运行、测试中有效,依赖传递
  4. test

    • 表示程序正常使用时不需要该依赖,只在测试编译和执行中有效。依赖不传递
  5. system

    • 和provided很像,但是必须明确指定jar路径,它不在仓库中去寻找
  6. import

执行ssh命令

2021-04-19
java

  • 如何使用ssh连接linux执行shell命令?
package space.xiaoxiang;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 通过ssh连接到linux主机,并执行一些shell命令,需要导入JSch这个包
 */
public class App {

    private static final String host = "localhost";
    private static final int port = 22;
    private static final String username = "xiaoxiang";
    private static final String password = "asdf";

    //连接到linux主机
    public Session connect() throws  JSchException {
        JSch jsch = new JSch();
        Session session = jsch.getSession(username, host, port);
        session.setPassword(password);
        session.setConfig("StrictHostKeyChecking", "no");
        //开启会话
        session.connect();
        return session;
    }

    //执行shell命令
    public void execCommand(Session session, String command) throws JSchException, IOException {
        ChannelExec channelExec = (ChannelExec) session.openChannel("exec");
        InputStream in = channelExec.getInputStream();
        channelExec.setCommand(command);
        BufferedReader reader = new BufferedReader(new InputStreamReader(channelExec.getInputStream()));
        channelExec.connect();
        String line;
        while((line = reader.readLine()) != null){
            System.out.println(line);
        }
        channelExec.disconnect();
    }

    public static void main(String[] args) throws IOException, JSchException {
        App app = new App();
        Session session = app.connect();
        app.execCommand(session, "ls -l");
        System.out.println("----------");
        app.execCommand(session, "df -h");
        session.disconnect();
    }
}

mybatis入门

2021-04-02
java

  1. 写mybatis配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!--数据库配置文件路径-->
    <!--<properties resource="config.properties"/>-->
    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--映射文件位置-->
    <mappers>
        <mapper resource="space/xiaoxiang/dao/XxxxMapper.xml"/>
        <!--<package name="space.xiaoxiang.dao"/> 使用package接口的包名必须与xml包名一样-->
    </mappers>
</configuration>
  1. 数据库配置文件config.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/xxxxx?serverTimezone=GMT%2B8&characterEncoding=utf-8
username=xxxx
password=xxxxxxxx
  1. 写dao层接口和xml文件

    1. 接口
    package space.xiaoxiang.dao;
    
    import java.util.List;
    import java.util.Map;
    
    public interface XxxxMapper {
        public List<Map<String, Object>> selectAll();
    }
    
    1. xml文件
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="space.xiaoxiang.dao.XxxxtMapper">
        <select id="selectAll" resultType="map">
            SELECT * FROM xxxx
        </select>
    </mapper>
    
  2. 运行

@Test
public void test() throws IOException {
    //数据库配置文件
    String databaseConfigFileName = "config.properties";
    Properties databaseConfig = new Properties();
    databaseConfig.load(Resources.getResourceAsStream(databaseConfigFileName));
    //mybatis配置文件
    String mybatisConfigFileName = "mybatis-config.xml";
    InputStream mybatisConfig = Resources.getResourceAsStream(mybatisConfigFileName);
    //一个数据库对应一个SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(mybatisConfig,databaseConfig);
    try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
        XxxxMapper xxxxMapper = sqlSession.getMapper(XxxxMapper.class);
        xxxxMapper.selectAll().forEach((map) -> {
            xxxxMapper.forEach((k, v) -> {
                System.out.println(k + " : " + v);
            });
        });
    }
}

zookeeper起步

2021-03-27
java

  • zkCli.sh -server localhost:2181:连接到zookeeper

  • 复制模式下的配置小结(以3台linux举例)

    1. 为每一台linux安装上zookeeper
    2. 修改他们的配置文件zoo.cfg(3份可以一样)
    3. 在dataDir路径下创建myid文件,里面写一个数字,与server.x的x对应
    4. 每一台都启动zookeeper
#zookeeper使用的基本时间单位
tickTime=2000
#存储内存数据库快照的位置
dataDir=/var/lib/zookeeper
#对client端提供服务
clientPort=2181
#超时
initLimit=5
#限制服务器与领导者之间过时的距离
syncLimit=2
#server.X格式列出了组成zookeeper服务的服务器。服务器启动时,它通过在数据目录中查找文件myid*来知道它是哪台服务器。
#2888集群内机器通信使用,3888选举leader使用
server.1=zoo1:2888:3888
server.2=zoo2:2888:3888
server.3=zoo3:2888:3888
  • zkServer.sh start:启动zookeeper
  • zkServer.sh status:查看当前状态
  • zkCli.sh -server 127.0.0.1:2181:连接到zookeeper
    • ls /
    • create /zk_test my_data:创建一个新的znode,并将字符串my_data与该节点关联
    • get /zk_test:验证数据是否与znode关联
    • set /zk_test junk:来更改与zk_test相关的数据
    • delete /zk_test

Znode #

  • 组成
    • data:Znode存储的数据信息
    • stat:包含Znode的各种元数据,如事务ID、版本号、时间戳、大小等
    • child:当前节点的子节点引用
    • acl:access control list访问控制表,表示哪些人或哪些ip可以访问本节点
  • Znode是用来存储少量的状态和配置信息,每个节点的数据最大不能超过1MB

clickhouse类型及函数

2021-03-25
数据库

类型 #

Decimal(P,S), Decimal32(S), Decimal64(S), Decimal128(S) #

  • 有符号的定点数,可在加减乘中保持精度,除法的最低有效数字会被丢弃(不舍入)

参数 #

  • P表示精度,有效范围是[1:38],决定可以有多少个十进制的数字
  • S表示规模,有效范围是[0:P],决定数字的小数部分中包含的小数位数
  • P[1:9]对应Decimal32(S)
  • P[10:18]对应Decimal64(S)
  • P[19:38]对应Decimal128(S)
  • 精度

函数 #

parseDateTimeBestEffort(time_string[, time_zone]) #

  • 把其他类型的时间日期转换为DateTime数据类型。
  • 支持的格式:timestamp YYYYMMDDHHMMSS DD/MM/YYYY hh:mm:ss YYYY-MM-DD hh:mm:ss

SHOW CREATE TABLE #

  • 返回创建该表的语句

类型转换 #

  • clickhouse是强类型,他不会在类型之间进行隐式转换。

toTypeName(x) #

  • 返回该参数的类型