0%

多二级/三级域名多服务nginx配置

常用命令

1
2
3
4
5
6
7
8
#启动
/usr/local/nginx/sbin/nginx
#停止
/usr/local/nginx/sbin/nginx -s stop
#检查脚本
/usr/local/nginx/sbin/nginx -t
#重新加载配置
/usr/local/nginx/sbin/nginx -s reload

nginx.conf

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#指定nginx 用户 组
#user sure sure;
#内容存进程的id,作用防止多个进程启动
pid /usr/local/nginx/conf/nginx.pid;
#错误日志(从左到右:debug最详细 crit最少)
#[ debug | info | notice | warn | error | crit ]
error_log /usr/local/nginx/logs/error.log crit;

#启动进程,通常设置成和cpu的数量相等,或者设置auto
worker_processes 24;
#在linux 2.6内核下开启文件打开数为65535
worker_rlimit_nofile 65535;

events {
#Epoll: 使用于Linux内核2.6版本及以后的系统
use epoll;
#所以nginx支持的总连接数就等于worker_processes * worker_connections
worker_connections 65535;
}

http {
#文件扩展名与类型对应关系
include mime.types;
#没有找到对应类型使用application/octet-stream
default_type application/octet-stream;

#访问日志关
access_log off;
# 隐藏nginx版本,防止通过版本漏洞攻击
server_tokens off;

server_names_hash_bucket_size 128;
client_header_buffer_size 32k;
large_client_header_buffers 4 32k;

sendfile on;
tcp_nopush on;
#连接超时时间
keepalive_timeout 120;
tcp_nodelay on;

gzip on;
gzip_min_length 1k;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;

#解决iframe跨域问题
add_header P3P "CP=CAO PSA OUR";

#解决页面部分缓存问题
ssi on;
ssi_silent_errors on;
ssi_types text/shtml;
ssi_types text/action;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $http_x_forwarded_for';
log_format mlnormal '$remote_addr|$remote_user|[$time_local]|$host|"$request"|'
'$status|$body_bytes_sent|"$http_referer"|'
'"$http_user_agent"|$http_x_forwarded_for|'
'$upstream_addr|$upstream_status|$upstream_response_time|'
'$server_addr';
include vhost/*.conf;
}

子配置目录vhost/*.conf,支持多个域名对应服务的配置文件,一个服务对应一个二级域名文件

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

#均衡代理,以及模式设置
upstream webcs {
# 过源IP进行HASH的机制。可以解决session问题
ip_hash;
#tomcat服务的访问地址
server 134.175.14.8:8042 max_fails=2 fail_timeout=30s;
server 134.175.151.120:8043 max_fails=2 fail_timeout=30s;
}

server {
listen 80;
#listen 443 ssl;
#域名访问地址
# 多个域名 server_name ... iexxk.com www.iexxk.com;
server_name outtest.iexxk.com;
index index.htm index.html index.jsp;
#tomcat webapp目录,或这dobase目录
root /data/web/webcs;

#ssl_certificate /data/ssl/shenqi.cer;
#ssl_certificate_key /data/ssl/shenqi.key;
#ssl_session_timeout 5m;

access_log /usr/local/nginx_logs/login.access.log mlnormal;

location ~ ^/WEB-INF/* {
deny all;
}

location ~ .(svn|git|cvs) {
deny all;
}

location ~.*\.(jsp|do|shtml)?$ {
#proxy_pass配置为:http:// + upstream策略名称
proxy_pass http://webcs;
proxy_next_upstream http_500 http_502 http_503 http_504 error timeout invalid_header;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
#include /usr/local/nginx/conf/proxy.conf;
}

location ~* \.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
expires 30d;
}
}

配置ssl/https

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
upstream res {
ip_hash;
server 172.16.16.8:14081 max_fails=2 fail_timeout=30s;
server 172.16.16.8:14081 max_fails=2 fail_timeout=30s;
}

server {
listen 80;
listen 443 ssl;
server_name outtest.res.suresvip.com;

#证书相关配置
ssl on; #注意这个用on其他所有改nginx配置的网站都会重定向到https,所以改用off不会影响其他的,但是不会自动重定向
ssl_certificate /usr/local/nginx/ssl/1_outtest.res.suresvip.com_bundle.crt;
ssl_certificate_key /usr/local/nginx/ssl/2_outtest.res.suresvip.com.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #按照这个协议配置
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;

#注释下面的不然使用https访问会下载文件
# index index.htm index.html index.jsp;
# root /data/web/res1;

access_log /usr/local/nginx_logs/login.access.log mlnormal;

location ~ ^/WEB-INF/* {
deny all;
}

location ~ .(svn|git|cvs) {
deny all;
}

location ~.* {
proxy_pass http://res;
proxy_next_upstream http_500 http_502 http_503 http_504 error timeout invalid_header;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
#include /usr/local/nginx/conf/proxy.conf;
}

location ~* \.(gif|jpg|jpeg|png|bmp|swf|js|css)$ {
expires 30d;
}
}

本地https配置

1
2
3
4
5
6
7
8
9
10
 server {
listen 9600 ssl;
server_name localhost;
ssl_certificate server.crt;
ssl_certificate_key server.key;
ssl_session_timeout 5m;
location / {
proxy_pass http://bpf_pf_access;
}
}

tomcat测试https

settings->Automatically follow redirects->Default: Settings->general->ssl certificate verification(off)->certificates->client certificates(添加证书文件即可)

nginx域名验证

  1. 阿里云申请证书

  2. 选择文件验证,下载文件复制类容

  3. 在nginx添加如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    server {
    listen 80;
    server_name iexxk.com;
    # return 301 https://$host$request_uri;
    #以下部分是验证用的,验证通过后可以删除,将刚刚复制的字符串替换200后的字符串
    location ^~/.well-known/pki-validation/fileauth.txt {
    add_header Content-Type 'text/html; charset=utf-8';
    return 200 '202207310000001mwx2sk9p9akvj3vg3b8sqew1uxxxxxxxxxx0f01ri34uddzzc';
    }
    }
  4. 点击验证,验证通过后,等待审核通过

  5. 下载nginx证书文件

  6. 上传证书文件到nginx服务器,配置nginx证书文件

    1
    2
    3
    4
    5
    6
    7
    server{
    listen 443 ssl;
    server_name iexxk.com;
    ssl_certificate /etc/nginx/ssl/8218253_prd-gva.mh.xxx.com.cn_nginx/8218253_iexxk.com.pem;
    ssl_certificate_key /etc/nginx/ssl/8218253_prd-gva.mh.xxx.com.cn_nginx/8218253_iexxk.com.key;
    ...
    }

原理

62oUdf.png

分片(shard)

分片是存储了一个集合部分数据的MongoDB实例,每个分片 可以是一台服务器运行单独一个Mongod实例,但是为了提高系统的可靠性实现自动故障恢复,一个分片应该是一个复制集。 通过分片,我们将一个集合拆分为多个数据块,这些数据块分别部署在不同的机器上,这样可以做到增加单台机器的磁盘可用空间,同时将查询分配到不同的机器上,减轻单台机器的负载。

路由(Router/mongos)

mongos是一个前置路由,我们的应用客户端并不是直接与分片连接,而是与mongos路由连接,mongos接收到客户端请求后根据查询信息将请求任务分发到对应的分片,在正式生产环境中,为确保高可用性,一般会配置两台以上的mongos路由,以确保当其中一台宕机后集群还能保持高可用。

配置(config)

配置服务器相当于集群的大脑,它存储了集群元信息:集群中有哪些分片、分片的是哪些集合以及数据块的分布集群启动后,当接收到请求时,如果mongos路由没有缓存配置服务器的元信息,会先从配置服务器获取分片集群对于的映射信息。同样的,为了保持集群的高可用,一般会配置多台配置服务器。

命令详解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mongod
--dbpath 数据库路径(数据文件)
--logpath 日志文件路径
--master 指定为主机器
--slave 指定为从机器
--source 指定主机器的IP地址
--pologSize 指定日志文件大小不超过64M.因为resync是非常操作量大且耗时,最好通过设置一个足够大的oplogSize来避免resync(默认的 oplog大小是空闲磁盘大小的5%)。
--logappend 日志文件末尾添加
--port 启用端口号
--fork 在后台运行
--only 指定只复制哪一个数据库
--slavedelay 指从复制检测的时间间隔
--auth 是否需要验证权限登录(用户名和密码)
--shardsvr 此实例为shard(分片),默认侦听27018端口
--configsvr 此实例为config server,默认侦听27019端口

基础知识

mongod 核心数据库进程

mongos 分片群集的控制器和查询路由器

mongo 交互式的MongoDB Shell

mongo命令行

1
2
3
4
5
6
7
8
9
10
#启动分片服务--shardsvr指定实例为分片,默认端口为27018
mongod --shardsvr --directoryperdb --replSet shard1
#进入分片服务命令行
mongo --port 27018
#启动配置服务--configsvr指定实例为配置,默认端口为27019
mongod --configsvr --directoryperdb --replSet config --smallfiles
#进入配置服务命令行
mongo --port 27019
#查看集群状态
mongos> sh.status()

docker集群搭建mongo分片服务

服务架构:

服务器两台组成docker swarm

  • mongo-server1-shard1:27018(内|shard1主)
  • mongo-server1-shard2:27018(内|shard2主)
  • mongo-server2-shard1:27018(内|shard1从)
  • mongo-server2-shard2:27018(内|shard2从)
  • mongo-config1:27019(内|主)
  • mongo-config2:27019(内|从)
  • mongo-router:27017(外|每个节点一个)

8个服务组成

搭建步骤(有顺序)

  1. 创建服务脚本并启动

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    mongo-server1-shard1:
    image: mongo
    restart: always
    command: mongod --shardsvr --directoryperdb --replSet shard1
    # ports:
    # - 14017:27017
    # environment:
    # MONGO_INITDB_ROOT_USERNAME: root
    # MONGO_INITDB_ROOT_PASSWORD: example
    volumes:
    - /data/v-yinfu/mongo/shard1/configdb:/data/configdb
    - /data/v-yinfu/mongo/shard1/db:/data/db
    deploy:
    replicas: 1
    restart_policy:
    condition: on-failure
    placement:
    constraints: [node.hostname == VM_16_8_centos]
    mongo-server1-shard2:
    image: mongo
    restart: always
    command: mongod --shardsvr --directoryperdb --replSet shard2
    volumes:
    - /data/v-yinfu/mongo/shard2/configdb:/data/configdb
    - /data/v-yinfu/mongo/shard2/db:/data/db
    deploy:
    replicas: 1
    restart_policy:
    condition: on-failure
    placement:
    constraints: [node.hostname == VM_16_8_centos]
    mongo-server2-shard1:
    image: mongo
    restart: always
    command: mongod --shardsvr --directoryperdb --replSet shard1
    volumes:
    - /data/v-yinfu/mongo/shard1/configdb:/data/configdb
    - /data/v-yinfu/mongo/shard1/db:/data/db
    deploy:
    replicas: 1
    restart_policy:
    condition: on-failure
    placement:
    constraints: [node.hostname == VM_16_13_centos]
    mongo-server2-shard2:
    image: mongo
    restart: always
    command: mongod --shardsvr --directoryperdb --replSet shard2
    volumes:
    - /data/v-yinfu/mongo/shard2/configdb:/data/configdb
    - /data/v-yinfu/mongo/shard2/db:/data/db
    deploy:
    replicas: 1
    restart_policy:
    condition: on-failure
    placement:
    constraints: [node.hostname == VM_16_13_centos]
    mongo-config1:
    image: mongo
    restart: always
    command: mongod --configsvr --replSet replConfig --directoryperdb --smallfiles
    volumes:
    - /data/v-yinfu/mongo/config1/configdb:/data/configdb
    - /data/v-yinfu/mongo/config1/db:/data/db
    deploy:
    replicas: 1
    restart_policy:
    condition: on-failure
    placement:
    constraints: [node.hostname == VM_16_8_centos]
    mongo-config2:
    image: mongo
    restart: always
    command: mongod --configsvr --replSet replConfig --directoryperdb --smallfiles
    volumes:
    - /data/v-yinfu/mongo/config2/configdb:/data/configdb
    - /data/v-yinfu/mongo/config2/db:/data/db
    deploy:
    replicas: 1
    restart_policy:
    condition: on-failure
    placement:
    constraints: [node.hostname == VM_16_13_centos]
    mongo-router:
    image: mongo
    restart: always
    command: mongos --configdb replConfig/mongo-config1:27019,mongo-config2:27019 --bind_ip 0.0.0.0 --port 27017
    ports:
    - 14017:27017
    volumes:
    - /data/v-yinfu/mongo/router/configdb:/data/configdb
    - /data/v-yinfu/mongo/router/db:/data/db
    depends_on:
    - mongo-config1
    deploy:
    mode: global
    # replicas: 1
    restart_policy:
    condition: on-failure

    初始化(添加)配置服务器

  2. 编写初始化脚本,首先初始化config配置服务,这里只需要初始化一个mongo-config1服务另一个会自动初始化

    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
    docker exec -it $(docker ps | grep "mongo-config1" | awk '{ print $1 }') bash -c "echo 'rs.initiate({_id: \"replConfig\",configsvr: true, members: [{ _id : 0, host : \"mongo-config1:27019\" },{ _id : 1, host : \"mongo-config2:27019\" }, ]})' | mongo --port 27019"
    #--------------------------等效于以下命令----------------------------
    #查看容器id
    docker ps
    #进入mongo-config1容器
    docker exec -it <mongo-config1容器id> bash
    #进入容器后,连接mongo
    mongo --port 27019
    #然后执行mongo初始化命令
    rs.initiate(
    {
    _id: "replConfig",
    configsvr: true,
    members: [
    { _id : 0, host : "mongo-config1:27019" },
    { _id : 1, host : "mongo-config2:27019" }
    ]
    }
    )
    # 校验可以进入容器
    mongo --port 27019
    # 可以看到两个容器前面分别是
    replConfig:PRIMARY>
    replConfig:SECONDARY>
    # 也可以通过这个命令查看
    rs.conf()

    其他常用命令

    1
    2
    3
    4
    #查看config配置
    rs.conf()
    #重置config配置,执行失败需要升级啥的看,代理方式删除挂载卷
    rs.reconfig()
  3. 校验检查mongo-router服务是否连接成功,可以查看Cannot reach any nodes for set replConfig这个日志是否停止,或者进入容器执行mongo校验,如果2没有初始化成功,执行mongo会报错,成功会进入mongos>命令行

    初始化(添加)分片服务器

  4. 初始化shard服务,还是两种方式,一是脚本,二是手动

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    #初始化shard1
    docker exec -it $(docker ps | grep "shard1" | awk '{ print $1 }') bash -c "echo 'rs.initiate({_id : \"shard1\", members: [{ _id : 0, host : \"mongo-server1-shard1:27018\" },{ _id : 1, host : \"mongo-server2-shard1:27018\" }]})' | mongo --port 27018"
    #初始化shard2
    docker exec -it $(docker ps | grep "shard2" | awk '{ print $1 }') bash -c "echo 'rs.initiate({_id : \"shard2\", members: [{ _id : 0, host : \"mongo-server1-shard2:27018\" },{ _id : 1, host : \"mongo-server2-shard2:27018\" }]})' | mongo --port 27018"
    #--------------等效于以下命令--------------------------------
    #进入容器省略
    #进入mongo-server1-shard1
    mongo --port 27018
    rs.initiate(
    {
    _id : "shard1",
    members: [
    { _id : 0, host : "mongo-server1-shard1:27018" },
    { _id : 1, host : "mongo-server2-shard1:27018" }
    ]
    }
    )

    #进入mongo-server1-shard2
    mongo --port 27018
    rs.initiate(
    {
    _id : "shard2",
    members: [
    { _id : 0, host : "mongo-server1-shard2:27018" },
    { _id : 1, host : "mongo-server2-shard2:27018" }
    ]
    }
    )
    #额外-----------------------
    //添加额外分片
    rs.add( { host: "mongodb3.example.net:27017", priority: 0, votes: 0 } )
    //添加仲裁
    rs.addArb("shard34:27018");
    //移除节点
    rs.remove("shard34:27018");
    //设置从分片可读
    db.getMongo().setSlaveOk();

    添加分片集群到mongos

  5. 添加分片集群到mogons,只用添加一个即可mongo-router,另一个自动就有了

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #添加shard1分片集群到mogons
    docker exec -it $(docker ps | grep "mongo-router" | awk '{ print $1 }') bash -c "echo 'sh.addShard(\"shard1/mongo-server1-shard1:27018,mongo-server2-shard1:27018\")' | mongo "
    #添加shard1分片集群到mogons
    docker exec -it $(docker ps | grep "mongo-router" | awk '{ print $1 }') bash -c "echo 'sh.addShard(\"shard2/mongo-server1-shard2:27018,mongo-server2-shard2:27018\")' | mongo "
    ###---------------------等效于以下命令-----
    mongo
    sh.addShard("shard1/shard11:27018,shard12:27018,shard13:27018")
    sh.addShard("shard2/shard21:27018,shard22:27018,shard23:27018")
    sh.addShard("shard3/shard31:27018,shard32:27018,shard33:27018")

  6. 查看片的状态sh.status()

    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
    28
    sharding version: {
    "_id" : 1,
    "minCompatibleVersion" : 5,
    "currentVersion" : 6,
    "clusterId" : ObjectId("5c4b110ca28b26d76cfee0e3")
    }
    shards:
    { "_id" : "shard1", "host" : "shard1/mongo-server1-shard1:27018,mongo-server2-shard1:27018", "state" : 1 }
    { "_id" : "shard2", "host" : "shard2/mongo-server1-shard2:27018,mongo-server2-shard2:27018", "state" : 1 }
    active mongoses:
    "4.0.5" : 2
    autosplit:
    Currently enabled: yes
    balancer:
    Currently enabled: yes
    Currently running: no
    Failed balancer rounds in last 5 attempts: 0
    Migration Results for the last 24 hours:
    No recent migrations
    databases:
    { "_id" : "config", "primary" : "config", "partitioned" : true }
    config.system.sessions
    shard key: { "_id" : 1 }
    unique: false
    balancing: true
    chunks:
    shard1 1
    { "_id" : { "$minKey" : 1 } } -->> { "_id" : { "$maxKey" : 1 } } on : shard1 Timestamp(1, 0)

    使能分片数据库

  7. sh.enableSharding("<database>")仅仅只是标记数据库使能分片

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    mongos> sh.enableSharding("test")
    {
    "ok" : 1,
    "operationTime" : Timestamp(1563271100, 3),
    "$clusterTime" : {
    "clusterTime" : Timestamp(1563271100, 3),
    "signature" : {
    "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
    "keyId" : NumberLong(0)
    }
    }
    }

    err:sun.reflect.GeneratedMethodAccessor109.invoke(Unknown Source)

  8. sh.shardCollection("<database>.<table>",{_id:1})

    1
    2
    3
    4
    5
    6
    db.table1.stats();
    {
    "sharded" : true,
    "shards" : {
    }
    }
  9. admin执行db.runCommand({"shardcollection":"app_test.MusicList","key":{"seat":1}})

  10. app_test执行db.MusicList.ensureIndex({"seat":1},{background: 1})

集群分析

模式:主+备+备+仲+仲(主备不区分) 宕机设备 mongo可读写
主+备+仲+仲 -备 可用 选举时间3~4秒(偶尔发生)
主+仲+仲 -备-备 可用 选举<1秒,且最后一个节点为主
主+仲 -备-备-仲 不可用 最后一个主降级为从
主+备+仲 -备-仲 可用
主+备 -备-仲-仲 不可用 无主
主+备+备 -仲-仲 可用

总结:

  1. 3节点(1注+2副本)可宕机一台,5节点模式(1主+2副本+2仲裁)可宕机2台

  2. 仲裁节点:等效于副本,参与选举,但是不能成为主节点,不存储数据

  3. 节点主副本其实是不区分的,启动时随机选一个做主节点,当发现宕机是会选举一个新的主节点,当宕机过多,不足以达到高可用,主节点会自动降级,当前主从副本就不会出现主节点,mongo存储数据就会提示错误,没有主节点

  4. 路由节点可以不用见主从

  5. 配置节点主从可以全部宕机也能正常写数据,但是数据不会自动分片,以及不能进行分片等设置查询操作,涉及配置操作都会失败

  6. 数据迁移(Sharded Cluster Balancer),当一个分片数据过多时,会发生数据平衡,让每个分片数据相差不大

  7. 数据回滚,当一个节点宕机,会通过oplog进行节点数据恢复,当数据大于oplog文件设置的大小(没设置是按磁盘的%比设置的)时,数据会被覆盖,4以前的版本,如果数据30分钟内没回滚完(未测),也会终止不会回滚,并提示错误,具体参考MongoDB副本集的工作原理

  8. 读写默认通过主节点,副本节点读数据需要设置副本可读

  9. 分片的设置,需要先使能库设置分片库,然后再设置分片表,删了表需要重新使能分片表

  10. 当数据达到一定大小才会开始分片(60M?)

  11. db.stats()默认byte,可以用参数db.stats(1024*1024)转换为mb

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
    "db" : "xxx", //当前数据库
    "collections" : 27, //当前数据库多少表
    "objects" : 18738550, //当前数据库所有表多少条数据
    "avgObjSize" : 1153.54876188392, //每条数据的平均大小 byte
    "dataSize" : 21615831152.0, //所有数据的总大小
    "storageSize" : 23223312272.0, //所有数据占的磁盘大小
    "numExtents" : 121,
    "indexes" : 26, //索引数
    "indexSize" : 821082976, //索引大小
    "fileSize" : 25691160576.0, //预分配给数据库的文件大小
    "nsSizeMB" : 16,
    "dataFileVersion" : {
    "major" : 4,
    "minor" : 5
    },
    "extentFreeList" : {
    "num" : 1,
    "totalSize" : 65536
    },
    "ok" : 1.0
    }

参考

docker-swarm部署mongo分片集群

Deploy a Sharded Cluster

MongoDB搭建分片集群

MongoDB副本集的工作原理

1
2
3
4
5
6
7
8
9
10
11
mongo:
image: mongo
restart: always
ports:
- 14017:27017
# environment:
# MONGO_INITDB_ROOT_USERNAME: root
# MONGO_INITDB_ROOT_PASSWORD: example
volumes:
- /dockerdata/v-yinfu/mongo/configdb:/data/configdb
- /dockerdata/v-yinfu/mongo/db:/data/db

solr清除数据

步骤:

  1. 登录solr网页

  2. 搜索对应的core

  3. 点击Documents –> Document Type –> 选择XML类型

  4. 定格编写语句:

    1
    2
    <delete><query>*:*</query></delete>
    <commit/>
  5. 最后点击Submit Document

安装

docker-compose.yml

1
2
3
4
5
solr:
image: solr:5.5.5
restart: always
ports:
- 14093:8983

访问ip:14093

常见问题

问题1: index已经锁定Caused by: org.apache.solr.common.SolrException: Index locked for write for core XXX

解决:

solr数据备份注意目录复制时权限

1
2
3
4
5
6
#删除这个文件
rm /data/soft/solr/data/index/write.lock
#以root权限进入容器执行
docker exec -it --user root <容器id> bash
cd /opt/solr/server/solr
chown -R solr:solr sure-core

安装ruby

1
2
3
4
5
6
7
8
9
10
#依赖环境
yum -y install gcc automake autoconf libtool make
#安装源
yum install centos-release-scl-rh
#安装ruby2.3
yum install rh-ruby23 -y
#使能ruby2.3(每次要重新执行)
scl enable rh-ruby23 bash
#查看版本2.3
ruby -v

安装redis

1
2
3
4
5
6
7
8
9
10
11
yum install wget
wget http://download.redis.io/releases/redis-3.2.6.tar.gz
tar xzf redis-3.2.6.tar.gz
mv redis-3.2.6 /opt/redis
make
#运行测试
/opt/redis/src/redis-server /opt/redis/redis.conf
# 使用redis-trib集群需要执行
gem install redis
# 创建集群
./src/redis-trib.rb create --replicas 1 192.168.101.108:7000 192.168.101.108:7001 192.168.101.108:7002

docker方式构建

部署脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
redis700x:
image: redis
restart: always
hostname: redis-master
command: "redis-server /data/redis.conf"
volumes:
- /dockerdata/v-yinfu/redis/7000:/data
ports:
- target: 700x
published: 700x
protocol: tcp
mode: host
- target: 1700x
published: 1700x
protocol: tcp
mode: host

配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
## 自定义配置
# redis:3.2.6需要打开
# bind 0.0.0.0
protected-mode no
port 700x
# cluster-announce-x系列参数redis:5-alpine才支持,3.2.6屏蔽
cluster-announce-port 700x
cluster-announce-bus-port 1700x
# 自己服务器的ip
cluster-announce-ip 192.168.101.108
daemonize no
appendonly yes
cluster-enabled yes
appendonly yes
cluster-config-file nodes.conf
cluster-node-timeout 15000

上面x换成0-6

1
2
3
4
# 创建集群
./redis-trib.rb create --replicas 1 192.168.101.108:7000 192.168.101.108:7001 192.168.101.108:7002 192.168.101.108:7003 192.168.101.108:7004 192.168.101.108:7005
# 检查集群
./src/redis-trib.rb check 192.168.101.108:7000

使用docker镜像脚本创建

1
2
3
4
5
6
7
8
#创建集群 主1 主2 主3 从1 从2 从3 的顺序来的
docker run -it --rm exxk/redis-trib ruby redis-trib.rb create --replicas 1 172.16.16.8:7000 172.16.16.8:7001 172.16.16.8:7002 172.16.16.13:7003 172.16.16.13:7004 172.16.16.13:7005
#检查集群状态
docker run -it --rm exxk/redis-trib ruby redis-trib.rb check 192.168.101.108:7000
# 查看集群负载
docker stats $(docker ps | grep "redis-cluster" | awk '{ print $1 }')
# 修复集群
docker run -it --rm exxk/redis-trib ruby redis-trib.rb fix 10.10.10.11:7000

高可用总结

  1. 执行集群creat脚本时,默认前三个为主后三个为从,主从对应关系随机分配
  2. 集群存在主从对应关系,一个主回自动分配一个从,集群宕机也不会改变
  3. 集群一个主从挂了不能访问
  4. 集群必须所有的主都能正常运行
  5. 集群从升级主存在时间间隔,试配置和性能等因素影响,可能长可能短,在没有主选择成功集群状态显示异常,且无法访问,提示错误Not all 16384 slots are covered by nodes.
  6. 集群同时宕机两个主,从是无法升级为主,这种情况只在同时,如果其中一个从升级主,再宕机一个主,是没有关系的
  7. 从节点宕机对主节点毫无影响

常见问题

问题1: 一直提示waiting for

1
2
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join..

解决:防火墙开端口

1
2
3
4
5
6
7
8
9
10
11
# 添加端口7000-7005/17000-17005
firewall-cmd --zone=public --add-port=7000/tcp --permanent
# 重载配置
firewall-cmd --reload
# 检查防火墙规则
firewall-cmd --list-all
# ports: 7000/tcp 7001/tcp 7002/tcp 7003/tcp 7004/tcp 7005/tcp 17005/tcp 17004/tcp 17003/tcp 17002/tcp 17001/tcp 17000/tcp
# 查看防火墙状态
firewall-cmd --state
# 临时关闭防火墙,重启后会重新自动打开
systemctl restart firewalld

然后删除服务及缓存文件重新启动

1
2
3
4
5
# 删除缓存文件
rm -f /dockerdata/v-yinfu/redis/700*/appendonly.aof
rm -f /dockerdata/v-yinfu/redis/700*/nodes.conf
# 重新集群
./redis-trib.rb create --replicas 1 192.168.101.108:7000 192.168.101.108:7001 192.168.101.108:7002 192.168.101.108:7003 192.168.101.108:7004 192.168.101.108:7005

问题2 版本兼容问题

redis5 不支持jedis2.8,会提示错误7001@17001,升级jedis3.0可以解决

redis5(4)一下不支持docker 集群模式

1
2
3
4
cluster-announce-port 700x
cluster-announce-bus-port 1700x
# 自己服务器的ip
cluster-announce-ip 192.168.101.108

参考

docker redis 集群(cluster)搭建

centos7安装redis4集群服务

官网:https://redis.io/documentation
中文官网:http://www.redis.cn/documentation.html

osmdroid 加载kml/kmz文件

  1. 首先增加导入compile 'com.github.MKergall:osmbonuspack:6.5.2'

  2. 添加代码

    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
    28
    29
    30
    31
    32
        class KmlLoader extends AsyncTask<Void, Void, Void> {
    ProgressDialog progressDialog = new ProgressDialog(context);
    KmlDocument kmlDocument;

    @Override
    protected void onPreExecute() {
    super.onPreExecute();
    progressDialog.setMessage("Loading Project...");
    progressDialog.show();
    }

    @Override
    protected Void doInBackground(Void... voids) {
    File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/osmdroid/"+Config.KMLFILENAME);
    kmlDocument = new KmlDocument();
    //加载kml文件修改为parseKMLFile
    kmlDocument.parseKMZFile(f);
    FolderOverlay kmlOverlay = (FolderOverlay)kmlDocument.mKmlRoot.buildOverlay(mapView, null, null,kmlDocument);
    mapView.getOverlays().add(kmlOverlay);
    return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
    progressDialog.dismiss();
    mapView.invalidate();
    BoundingBox bb = kmlDocument.mKmlRoot.getBoundingBox();
    mapView.zoomToBoundingBox(bb, true);
    // mapView.getController().setCenter(bb.getCenter());
    super.onPostExecute(aVoid);
    }
    }
  3. 引用代码new KmlLoader().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

shp kml kmz文件转换可以用Google earth桌面软件

或者Tools-geoserver-base

idea远程debug总结

springboot

启动参数添加-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

docker环境配置:

1
CMD ["java","-jar","-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005","-Dspring.profiles.active=sit","app.jar"]

idea springboot 远程调试模式之本地
idea基于springboot远程调试之docker环境

tomcat

catalina.sh文件添加CATALINA_OPTS="-Xdebug -Xrunjdwp:transport=dt_socket,address=5005,suspend=n,server=y"

docker环境配置:

1
2
environment:
CATALINA_OPTS: "-Xdebug -Xrunjdwp:transport=dt_socket,address=5005,suspend=n,server=y"

Spring AOP

基于动态代理实现(动态生成插入方法invoke)

术语

  • 连接点(Join point): 能够拦截(插入方法)的地方

  • 切点(Poincut): 具体定位的连接点

  • 增强/通知(Advice):表示添加到切点的一段逻辑代码,并定位连接点的方位信息

    Spring AOP提供了5种Advice类型给我们:前置@Before、后置@After、返回@AfterReturning、异常@AfterThrowing、环绕@Around给我们使用!

  • 织入(Weaving):将增强/通知添加到目标类的具体连接点上的过程

  • 引入/引介(Introduction):允许我们向现有的类添加新方法或属性。是一种特殊的增强!

  • 切面(Aspect):切面由切点和增强/通知组成,它既包括了横切逻辑的定义、也包括了连接点的定义。

切入点定义

  1. execution 表达式

    eg: @Pointcut("execution(public * com.exxk.aop..*Controller.*(..))")

    *代表任意值

    aop..表示aop包下面的任何子包和自己

    (..)表示任何参数

  2. @annotation注解

    eg: @annotation(com.willson.common.annotation.Log)

注解类名字解释

@Target :

说明了Annotation所修饰的对象范围,eg@Target({ ElementType.PARAMETER, ElementType.METHOD })其中ElementType的取值有

  • CONSTRUCTOR:用于描述构造器
  • FIELD:用于描述域
  • LOCAL_VARIABLE:用于描述局部变量
  • METHOD:用于描述方法
  • PACKAGE:用于描述包
  • PARAMETER:用于描述参数
  • TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention

定义了该Annotation被保留的时间长短,eg@Retention(RetentionPolicy.RUNTIME),其中Retention取值有

  • SOURCE:在源文件中有效(即源文件保留)
  • CLASS:在class文件中有效(即class保留)
  • RUNTIME:在运行时有效(即运行时保留)
@Documented

用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

springboot aop log实战

  1. 引入依赖org.springframework.boot:spring-boot-starter-aop

  2. 定义一个接口作为测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.exxk.aop;
    @RestController
    @RequestMapping("/aop/")
    public class AopController {
    private Logger logger=Logger.getLogger(String.valueOf(getClass()));
    @GetMapping("test")
    //@Log(tag = "我是注解")
    public String aop(){
    logger.info("业务代码执行中...");
    return "aop 测试";
    }
    }
  3. 定义aop切面类

    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
    @Aspect
    @Component
    public class LogAspect {
    private Logger logger=Logger.getLogger(String.valueOf(getClass()));

    //定义切入点,切入点定义注意,某些类不能动态代理
    @Pointcut("execution(public * com.exxk.aop.AopController.aop())")
    public void pointCut(){}

    //切入点前插入的内容
    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint){
    logger.info("切入点前执行的内容:");
    ServletRequestAttributes attributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    if (attributes != null) {
    HttpServletRequest request=attributes.getRequest();
    logger.info("URL:"+request.getRequestURL().toString());
    }
    }

    @AfterReturning(returning = "ret",pointcut = "pointCut()")
    public void doAfterReturning(Object ret){
    logger.info("切点运行完之后执行的内容:");
    logger.info("response:"+ret);
    }
    }
  4. 调用接口之后打印日志

    1
    2
    3
    4
    5
    com.exxk.aop.LogAspect             : 切入点前执行的内容:
    com.exxk.aop.LogAspect : URL:http://127.0.0.1:8080/aop/test
    com.exxk.aop.AopController : 业务代码执行中...
    com.exxk.aop.LogAspect : 切点运行完之后执行的内容:
    com.exxk.aop.LogAspect : response返回:aop 测试
  5. 添加自定义注解类

    1
    2
    3
    4
    5
    @Target({ ElementType.PARAMETER, ElementType.METHOD })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Log {
    String tag() default "";
    }
  6. 如果要使用注解格式修改切入点为注解

    1
    2
    3
    4
    5
    //定义切入点
    //@Pointcut("execution(public * com.exxk.aop..*Controller.*(..))")
    //@Pointcut("execution(public * com.exxk.aop.AopController.aop())")
    @Pointcut("@annotation(com.exxk.aop.Log)")
    public void pointCut(){}
  7. 在接口上添加注解@Log(tag = "我是注解")

  8. 在切入点,eg:doBefore添加获取注解的内容

    1
    2
    3
    4
    5
    6
    //获取注解
    Signature signature = joinPoint.getSignature();
    MethodSignature methodSignature = (MethodSignature) signature;
    Method method = methodSignature.getMethod();
    Log log= method.getAnnotation(Log.class);
    logger.info("获取注解内容:"+log.tag());
  9. 最后访问测试,打印日志如下

    1
    2
    3
    4
    5
    6
    com.exxk.aop.LogAspect             : 切入点前执行的内容:
    com.exxk.aop.LogAspect : URL:http://127.0.0.1:8080/aop/test
    com.exxk.aop.LogAspect : 获取注解内容:我是注解
    com.exxk.aop.AopController : 业务代码执行中...
    com.exxk.aop.LogAspect : 切点运行完之后执行的内容:
    com.exxk.aop.LogAspect : response返回:aop 测试

优化注解

  1. CGLIB实现AOP在application.properties配置文件添加

    1
    2
    spring.aop.auto=true
    spring.aop.proxy-target-class=false

参考

Spring Boot中使用AOP统一处理Web请求日志

使用Spring Boot的AOP处理自定义注解

java 自定义注解之ElementType.PARAMETER

使用Spring AOP记录Controller层操作日志

Spring AOP就是这么简单啦

docker+nginx(vue)获取真实ip

nginx必须安装--with-http_realip_module通过此命令2>&1 nginx -V | tr -- - '\n' | grep http_realip_module进行检查

nginx -V 可以查看到的编译参数和编译的模块(静态和动态)

  1. nginx设置代理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    server {
    listen 8080;
    server_name localhost;

    location / {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    root /usr/share/nginx/html;
    index index.html index.htm;
    }
    }
  2. 代码设置

    1
    2
    3
    4
    5
    6
    public RestResult login(@RequestBody User user,HttpServletRequest request) {
    String ip = request.getHeader("X-Real-IP");
    if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
    ip = request.getRemoteAddr();
    }
    }
  3. 请求头设置

    1
    2
    3
    4
    5
    POST /app/index/login HTTP/1.1
    Host: 192.168.1.230:14083
    Content-Type: application/json
    X-Real-IP: 192.16.1.1
    cache-control: no-cache

经测试:

只需2,3设置即可,1设置无效,如果只设置1和3还是不是真实ip,所以变量remote_addr不是真实ip(该ip实际从哪里来待确定?),X-Real-IP是自定义header头,相当于key,要一致

总结

方法一:

由于nginx安装在docker集群,nginx获取的ip(remote_addr)总是某个(不确定是哪里来的)的ip(10.255.0.3),因此1设置无效

解决nginx服务采用host模式,端口配置采用

1
2
3
4
5
ports:
- target: 8888
published: 14881 #只有worker能访问该端口
protocol: tcp
mode: host #版本要求3.2

采用了host就没了负载均衡了

方法二:

是用客户端添加自定义头X-Real-IP但是前端请求暂时加不进去,且后端要修改获取ip的方法

参考

在使用了NGINX的时候,如何获取访问用户的IP

Unable to retrieve user’s IP address in docker swarm mode

三大主流分布式计算系统

Hadoop

Hadoop常用于离线的复杂的大数据分析处理

Hadoop采用MapReduce分布式计算框架,并根据GFS开发了HDFS分布式文件系统,根据BigTable开发了HBase数据存储系统。

Spark

Spark常用于离线的快速的大数据处理

Spark使用内存来存储数据

Storm

Storm常用于在线的实时的大数据处理

Storm不进行数据的收集和存储工作,它直接通过网络实时的接受数据并且实时的处理数据,然后直接通过网络实时的传回结果。

参考

主流的三大分布式计算系统:Hadoop,Spark和Storm