Java微服务多主机搭建尝试

前言

本文旨在利用两台云服务器实践各种技术栈的分布式部署,仍以酒店展示与酒店管理项目为主体,但进行了分布式拓展,因此架构如下图所示:
在这里插入图片描述

Let’s do it!

非局域网间使用swarm配置overlay进行容器通信的坑实在太多了,排到最后也没有排干净,放弃了,如果对排坑过程感兴趣,或者你也想实践一下在非局域网间自己搭建一个“局域网”(bushi),请移步我的排坑博客

下面,让我们开始更为简单方便的非局域网集群工作吧!核心思想就是IP直连来搭建集群以及限制IP访问来保护资源

仍然采用docker stack deploy的方式来部署,因为可以直接跨主机部署容器,简单高效,有关于这方面的内容依然可以参考上面列举的排坑博客,下面让我们开始吧!

第一部分:集群的搭建

搭建Nacos集群,挂载nginx负载均衡

流程总结为:

  • 搭建mysql数据库,并在对应库中执行nacos集群必备的sql语句建立table
  • 执行yml文件,在Server1和Server2上分别建立两个nacos集群服务,在Server1上建立nginx代理服务
  • 测试连接

启动nacos集群和nginx代理服务的yml文件如下:

# Use root/example as user/password credentials
version: '3.1'

services:
  nacos1:
    image: nacos/nacos-server:latest
    volumes:
      - /usr/local/nacos2/logs:/home/nacos/logs
    ports:
      - "8848:8848"
      - "9848:9848"
      - "9555:9555"
    env_file:
      - ../env/nacos-ip.env
    environment:
      - NACOS_SERVER_IP=[Server1_IP]
    restart: on-failure
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.hostname == [Server1_Hostname]
  nacos2:
    image: nacos/nacos-server:latest
    volumes:
      - /usr/local/nacos2/logs:/home/nacos/logs
    ports:
      - "8849:8848"
      - "9849:9848"
    env_file:
      - ../env/nacos-ip.env
    environment:
      - NACOS_SERVER_IP=[Server2_IP]
    restart: always
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.hostname == [Server2_Hostname]
  nginx:
    image: nginx:latest
    volumes:
      - /usr/local/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
      - /usr/local/nginx/logs:/var/log/nginx
      - /usr/local/nginx/conf.d:/etc/nginx/conf.d
      - /usr/local/nginx/conf/log_format.conf:/etc/nginx/log_format.conf
    ports:
      - "8080:8080"
    restart: always
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.hostname == [Server1_Hostname]

备注:

  • docker stack deploy启动的服务的日志不好查找,但正如我们所说,可以通过docker logs去查看服务启动的容器的日志,该方式适用于那些错误启动的容器,用来排查启动中的参数错误;也可以通过挂载卷的形式将启动日志输出到宿主机文件中,该方式适用于那些因未知原因无法启动的容器,用来排查启动错误

有关于nginx挂载卷的配置,可以参考文章:docker启动nginx的标准挂载配置

  • 由于外网和内网的问题,nacos在没有配置环境值NACOS_SERVER_IP时,优先以网卡IP作为Leader节点(否则集群无法生效),这就会导致多出一个节点的同时,该节点还不可用(因为是内网)。要解决该问题就需要在yml文件中添加环境值NACOS_SERVER_IP=[Server_IP]
搭建RabbitMQ集群

在搭建RabbitMQ集群时

而与nacos所不同的是,RabbitMQ构建集群时并未给我们提供修正IP和端口的接口,我们无法对其发送和接收的端口进行修正。这意味着如果是两台外网主机,使用Overlay进行搭建时由于端口限制,必须将两个RabbitMQ的暴露端口映射到与默认配置不同的位置,这就导致了数据共享的不可行

因此,在两台外网主机上搭建RabbitMQ集群时,我们不能通过docker stack deploy一键部署,而应在两台主机上进行分别部署,以暴露与默认端口匹配的相应外网端口

分别在Server1和Server2执行docker run命令,命令中需要注意的有两点:

  • /var/lib/rabbitmq文件夹和/etc/hosts都映射了出来,目的是方便查看/var/lib/rabbitmq文件夹中的.erlang.cookie文件是否匹配,以及配置/etc/hosts为外网IP与主机号。(.erlang.cookie文件不匹配时,容易报错TCP connection succeeded but Erlang distribution failed
  • 额外开放4369和25672端口作为集群访问的端口(外网原因,必须将端口映射出来,如果是局域网,这些端口在开放防火墙的前提是可以直接访问的)
docker run -d --hostname rabbitmq01 --name rabbitmqCluster01 -v /usr/local/rabbitmq01/rabbitmq:/var/lib/rabbitmq -v /usr/local/rabbitmq01/etc/hosts:/etc/hosts -p 15672:15672 -p 5672:5672 -p 4369:4369 -p 25672:25672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' rabbitmq:3.7-management

docker run -d --hostname rabbitmq02 --name rabbitmqCluster02 -v /usr/local/rabbitmq02/rabbitmq:/var/lib/rabbitmq -v /usr/local/rabbitmq02/etc/hosts:/etc/hosts -p 15672:15672 -p 5672:5672 -p 4369:4369 -p 25672:25672 -e RABBITMQ_ERLANG_COOKIE='rabbitmqCookie' rabbitmq:3.7-management

/etc/hosts的配置如下所示:

[server1外网IP] rabbitmq01
[server2外网IP] rabbitmq02

容器启动后,在Server1中进入容器rabbitmqCluster01,执行重载命令:

rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl start_app
exit

在Server2中进入容器rabbitmqCluster02,执行重载和加入集群命令:

rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster --ram rabbit@rabbitmq01
rabbitmqctl start_app
rabbitmqctl cluster_status
exit

此时可以看到集群中两个节点加入完成

此时两个MQ节点是普通集群模式,虽然互相之间可以看到队列和交换机的配置信息,但无法备份消息,节点间还没有实现数据的同步设置。
因此需要配置以下规则,保证所有数据都需要在两个节点间同步=,实现镜像集群:

rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'

在镜像集群模式下,创建队列的节点作为主节点,任何对该队列信息的访问都必须路由到该节点处理,镜像节点仅是起到数据安全性的保障。(当然,通过代码控制,在各个节点上负载均衡的创建队列,镜像模式自然可以起到应对高并发场景的功能)

搭建Elasticsearch集群

Elasticsearch集群的搭建相比于RabbitMQ会更简单一点,因为它开放了多种地址信息的配置:

  • 本地监听地址:用于确定本机elasticsearch
  • 发布地址:用于向外发布信息
  • 集群节点地址:用于确定集群信息

虽然相比于Nacos进行跨域集群的配置要更为复杂一些,但原理基本一致,而且只要配置好发布地址和集群节点地址,我们的Elasticsearch集群就可以通过外网开始集群工作了

在两个服务器上通过下面的批处理程序start.sh来启动Elasticsearch,启动后将自动建立集群关系:

sudo mkdir -p /usr/local/elasticsearch/config
sudo mkdir -p /usr/local/elasticsearch/data
sudo mkdir -p /usr/local/elasticsearch/logs
sudo mkdir -p /usr/local/elasticsearch/plugins

sudo chmod -R 775 /usr/local/elasticsearch
sudo cat <<EOF > /usr/local/elasticsearch/config/stack.yml

version: '3.7'
services:
  es01:
    image: elasticsearch:7.12.1
    environment:
      - network.bind_host=0.0.0.0
      - network.publish_host=Server1_IP
# 以下两行命令是对跨域访问的支持,*代表支持所有跨域IP访问,而http.cors.enabled默认是false,因此需自行配置
      - http.cors.enabled=true 
      - http.cors.allow-origin=*
      - node.name=es01
      - cluster.name=es-docker-cluster
      - discovery.seed_hosts=Server2_IP:9300
      - cluster.initial_master_nodes=Server1_IP:9300,Server2_IP:9300
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - /usr/local/elasticsearch/data:/usr/share/elasticsearch/data
      - /usr/local/elasticsearch/logs:/usr/share/elasticsearch/logs
      - /usr/local/elasticsearch/plugins:/usr/share/elasticsearch/plugins
    ports:
      - 9200:9200
      - 9300:9300
EOF

docker-compose -f /usr/local/elasticsearch/config/stack.yml up

Error 1. "failed to resolve host [\"Server_IP:Port\"]"

报错信息不是无法连接,而是无法解析,这是yml文件的读取特点,它不会将"“处理成字符串的标识(yml默认所有的类型都是string),因此其会通过转义字符将”“解析,服务器读取到的IP地址就成了"Server_IP:Port”。去掉yml文档中添加的无效""即可

Error 2. "connnect timeout 30s"

这个就是无法连接的问题了,依次查看以下三个信息:

  • 是否将9300端口映射出来
  • 防火墙是否放开9300端口
  • 是否配置外网发布地址,并允许跨域访问
三个集群建立中的知识总结

总的来说,集群的建立就分为两步:

  • 在各个服务器声明单个节点
  • 节点间进行信息交互,建立集群

我们根据节点间进行信息交互的设置方式,将集群建立分为两类:

  • 第一类(简单类):在启动阶段可由用户自主配置集群节点的IP与端口,自动建立集群关系,如:nacos, elasticsearch
  • 第二类(复杂类):在启动阶段没有提供配置集群节点的接口,集群关系建立需进入容器使用对应的命令,如:rabbitmq

对于第一类,我们需要注意以下信息:

  • 用于内部访问的所有端口是否都暴露,如果没有请暴露至外网,否则将无法建立跨域集群
  • 各个必须的端口间是否有默认规则,如果有请遵守。如果因为特殊原因不能遵守,需要注意是否有开放对应的配置接口,否则集群对接将失效
    • 比如nacos,9848端口就是根据8848+100得到,如果不遵守,而nacos又没有开放对应接口,此时就会报错
  • 当需要跨域建立集群时,是否默认开放了跨域请求,如果没有,是否有相关的配置接口

对于第二类,除上述注意点外,我们还需关注:

  • 主机地址的配置,必须进入容器配置hosts,将其他节点的外网IP添加进来

总的来说,在以后我们进行集群设计时,也应尽量学习nacos的配置简单以及elasticsearch的接口丰富,尽量避免rabbitmq这类繁复的集群建立方法

第二部分:微服务的部署

部署的过程按照:

  1. 打包各模块
  2. 形成模块+Dockfile+yml的文件夹,上传该部署资料
  3. 通过docker-compose命令启动容器,记得设置Gateway的默认端口映射,以便于外网访问

部署前一定要确保本地验证没有问题

总结

到这里,跨主机的微服务部署算是完成了,说说使用体验吧:

  • 第一次访问时,hotel_demo进行酒店信息展示的速度很慢,但第一次访问后,不管是翻页速度,查询速度都还比较满意。究其原因还是elasticsearch的跨域分片导致的内部查询延时过高,而第一次访问除了需要加载酒店数据外,还需要加载过滤信息,因而导致载入缓慢问题
  • hotel_admin同样有相同的问题,但延时较hotel_demo低一些

要解决这些问题,主要是从两方面考虑:

  • 集群部署不应该跨域,而是在同一局域网内,保证集群内部信息沟通的畅通与快速
  • 应增添分布缓存功能,提高用户的频繁访问时良好体验

附录

es索引

首先将es索引纪录在此:

PUT /hotel
{
  "settings": {
    "analysis": {
      "analyzer": { 
        "text_analyzer": { 
          "tokenizer": "ik_max_word",
          "filter": "py"
        },
        "completion_analyzer": { 
          "tokenizer": "keyword",
          "filter": "py"
        }
      },
      "filter": {
        "py": { 
          "type": "pinyin",
          "keep_full_pinyin": false,
          "keep_joined_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "remove_duplicated_term": true,
          "none_chinese_pinyin_tokenize": false
        }
      }
    },
    "number_of_shards": 3,
    "number_of_replicas": 1
  }, 
  "mappings": {
    "properties": {
      "id": {
        "type": "keyword",
        "index": false
      },
      "name" :{
        "type": "text",
        "analyzer": "text_analyzer",
        "search_analyzer": "ik_smart",
        "copy_to": "select"
      },
      "address": {
        "type": "keyword",
        "index": false  
      },
      "price": {
        "type": "integer"
      },
      "score": {
        "type": "integer"
      },
      "brand": {
        "type": "keyword",
        "copy_to": "select"
      },
      "city": {
        "type": "keyword",
        "copy_to": "select"
      },
      "starName": {
        "type": "keyword",
        "copy_to": "select"
      },
      "business": {
        "type": "keyword",
        "copy_to": "select"
      },
      "location": {
        "type": "geo_point"
      },
      "pic": {
        "type": "keyword",
        "index": false
      },
      "select": {
        "type": "text",
        "analyzer": "text_analyzer",
        "search_analyzer": "ik_smart"
      },
      "isAD": {
        "type": "boolean"
      },
      "suggestion": {
        "type": "completion",
        "analyzer": "completion_analyzer",
        "search_analyzer": "keyword"
      }
      }
    }
  }
}

连接数据库Tips

连接数据库时注意使用utf后缀,否则可能会出现数据库回写时中文变?号的错误:

?useUnicode=true&characterEncoding=utf8&useSSL=false
maven打包时跳过测试

修改pom.xml文件,增加跳过测试字段

<properties>
    <skipTests>True</skipTests>
</properties>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值