docker+phantomjs+haproxy 搭建phantomjs集群

  目标:

    搭建一个远程的phantomjs服务器,提供高可用服务,支持并发。

  原料:

    1、docker环境、docker-compose环境

    2、phantomjs镜像: docker.io/wernight/phantomjs

    3、haproxy镜像:haproxy:latest

  docker-compose 项目目录结构

  phantomjs/

    haproxy/

      haproxy.cfg

    docker-compose.yml

  配置文件内容

  docker-compose.yml 配置

version: "2"
services:
    phantomjs1:
        image: docker.io/wernight/phantomjs
        ports:
            - "8910"
        command: phantomjs --webdriver=8910 --cookies-file=/cookies.txt
        restart: always
        # 内存限制 单位 bytes 大B
        mem_limit: 2000000000
        expose:
            - "8910"

    phantomjs2:
        image: docker.io/wernight/phantomjs
        ports:
            - "8910"
        command: phantomjs --webdriver=8910 --cookies-file=/cookies.txt
        restart: always
        # 内存限制 单位 bytes 大B
        mem_limit: 2000000000
        expose:
            - "8910"

    haproxy:
        image: haproxy:latest
        volumes:
            - ./haproxy:/haproxy-override
            - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
        links:
            - phantomjs1
            - phantomjs2
        restart: always
        ports:
            - "8910:8910"
            - "8911:8911"
        expose:
            - "8910"
            - "8911"

  haproxy.cfg 配置内容

global
  log 127.0.0.1 local0
  log 127.0.0.1 local1 notice

defaults
  log global
  mode http
  #option httplog
  option dontlognull
  # option  redispatch # 后端挂掉 则重定向别的机器
  retries 5  # 连续5次检查失败 则判定不可用
  timeout connect 5000ms
  timeout client 50000ms
  timeout server 50000ms
  timeout check 20s  # 超时20s才判定服务不可用

listen stats
    bind 0.0.0.0:8911
    stats enable
    stats uri /

frontend balancer
    bind 0.0.0.0:8910
    mode http
    default_backend phantomjs_backends

backend phantomjs_backends
    mode http
    option forwardfor
    #balance source
    balance hdr(Cookie)  # 必须用这个 爬虫方面做了hook
    #balance url_param sessionId check_post 64
    server phantomjs1 phantomjs1:8910 check inter 10000  # 增加check检查间隔10s
    server phantomjs2 phantomjs2:8910 check inter 10000
    option httpchk GET /status
    http-check expect status 200

  配置完毕

  在phantomjs目录下执行

    启动命令  docker-compose up -d

    停止命令  docker-compose stop

  使用phantoms服务:

    http://机器ip:8910

  查看集群状态

    http://机器ip:8911

  

  下面天坑:

    一个一个看:

      1、python selenium远程连接phantomjs服务时使用的http链接不支持类似cookie、session之类的会话机制,

    而phantomjs由于使用了haproxy做负载均衡,haproxy默认是轮询后端服务器处理请求,每次请求都会定向到不同的

    后端服务器。所以selenium在第一次请求发起新建phantomjs session的命令,获取了 phantomjs sessionId

    之后,再次使用sessionId来操作phantomjs的时候,由于请求被发送到了不同的后端服务器,导致无法找到相应

    sessionId的资源,所以根本无法使用。而haproxy其他的负载均衡策略基本也都不可用。

      先明确一下我想达到的效果:

        1)第一次请求(新建phantomjs session)是随机分配,并且均匀分布的

        2)后续请求除非服务器挂掉,否则不能更改服务器(挂掉没办法,本次操作肯定中断了,得重新开始)

      下面逐个分析一下haproxy的负载均衡策略:

        1)roundrobin  默认轮询   不可用

        2)static-rr  根据权重  不可用(权重这个东西并不能保证绝对不换机器)

        3)leastconn 最少连接 呵呵

        4)source  对来源ip做hash  不可用(除非我的来源ip均匀分布,并且请求频率均匀分布,要不然

              肯定负载肯定会集中分布在某几台机器上)

        5)uri  对请求的url?前的部分或全部做hash 不可用(每次进行的操作都差不多,访问的api并不均匀分布)

        6)url_param  根据指定的GET参数(或POST参数)做hash  不可用 (第一次请求的时候木有sessionId 。。。)

        7)hdr(name) 根据指定的header(如user-agent)做hash  不可用 (selenium请求无状态 每个 + 每次 请求

              的header都一毛一样,还不让修改,不过我最终选的还是这个,后面会介绍如何修改

        8)rdp-cookie(name)  根据cookie来选择 不可用 ( selenium请求无状态

      下面是放大招的时刻:

        经过上面的分析,貌似没啥办法了,不过经过我苦思冥想,埋头研究selenium源码,终于发现了一个可以在不修改源码

      的情况下修改每次远程调用phantomjs api服务时发送请求的header的方法。废话不多说,上代码:

# coding:utf8
from selenium.webdriver.remote import remote_connection
# hook
import base64

class MyRemoteConnection(object):
    @classmethod
    def get_remote_connection_headers(cls, parsed_url, keep_alive=False):
        """
        Get headers for remote request.

        :Args:
         - parsed_url - The parsed url
         - keep_alive (Boolean) - Is this a keep-alive connection (default: False)
        """

        headers = {
            ‘Accept‘: ‘application/json‘,
            ‘Content-Type‘: ‘application/json;charset=UTF-8‘,
            ‘User-Agent‘: ‘Python http auth‘
        }

        if parsed_url.username:
            base64string = base64.b64encode(‘{0.username}:{0.password}‘.format(parsed_url).encode())
            headers.update({
                ‘Authorization‘: ‘Basic {}‘.format(base64string.decode())
            })

        if keep_alive:
            headers.update({
                ‘Connection‘: ‘keep-alive‘
            })
        # 下面这几行是我加的  重点在于keep_alive的非严格限制 以及可以在创建
        # remote driver是传递
        headers.update({
            "Cookie": keep_alive,
        })
        return headers

# 覆盖selenium包中的对应方法
remote_connection.RemoteConnection.get_remote_connection_headers = MyRemoteConnection.get_remote_connection_headers

      原理:

        selenium.webdriver.remote.remote_connection中有个类 RemoteConnection的get_remote_connection_headers

      方法控制每次调用api时使用的header,并且还接受一个参数 keep_alive,更重要的是 keep_alive参数在创建remote

      driver的时候可以传递,更更重要的是这个keep_alive 参数无论在哪里都只检查bool值,而不是具体值,所以我们可以把

      它作为一个唯一标识符,来放到header中,并在haproxy中做对应值的检查,只要生成keep_alive的算法是均匀分布的,就

      完美满足了我的要求。

        于是我选择了header中的Cookie值,在代码运行前动态hook这个方法,将keep_alive放入header中的Cookie值,然

      后在创建webdriver对象的时候生成一个唯一的keep_alive值传递进去,见代码:

# coding:utf8
import time
import hashlib
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium import webdriver

desired_capabilities = DesiredCapabilities.PHANTOMJS.copy()
browser = webdriver.Remote(
    command_executor=‘http://localhost:8910‘,
    desired_capabilities=desired_capabilities,
    # 被hook 作为 唯一标示
    keep_alive="{}".format(hashlib.md5("{}".format(time.time()).encode()).hexdigest())
)

      2、phantomjs内存无限制的话,跑着跑着docker宿主机就挂了,所以需要在docker-compose配置文件中对每个phantomjs容器

    增加内存限制、以及自动容器挂掉重启。

      3、haproxy会对容器健康情况做检查,这里需要对检查间隔和检查方法都做一下调整,放点水,要不然phantomjs容器很快就会内存

    爆掉,然后重启,然后你的连接断开,然后你的程序一多半时间都在尝试连接中度过。

       为啥内存会爆掉呢?还得说说haproxy负载均衡的问题,由于phantomjs服务在负载高的情况下响应速度比较慢,如果你对容器健康

    情况的检查很严格,那么就会经常把一个webdriver的请求发送到其他的服务器上,因为原来的服务器经常貌似不可用,于是原来的服务器

    上就会残留很多没有被关闭的session,一直在吃内存。。。,于是很快内存就不够了,然后容器挂掉,重启,然后同样的一幕继续上演。。

        针对这个情况,对haproxy做了如下配置:

        1)禁用转发 机器挂掉也不换机器 就这么任性的等机器恢复

          # option redispatch  # 后端挂掉 则重定向请求到别的服务器

        2)增加健康检查失败判定不可用的次数

          retries 5 # 失败5次后才判定此服务器不可用

        3)增加健康检查超时时间

          timeout check 20s

        4)增加健康检查间隔

          server phantomjs1 phantomjs1:8910 check inter 10000 # 检查间隔设置为10S

        5)使用 /status 接口来判定服务是否正常

          option httpchk GET /status

          http-check except status 200

  总结:

    坑 1 很重要,解决了集群可用性问题

    坑 2 3 也很重要,解决了集群稳定性问题

    最后,不要忘记对 docker-compose 增加一个定时重启的任务,我是用的crontab 每20分钟重启一次,要不然机器的负载蹭蹭的涨、内

  存哗哗的掉,一会就等着收服务器的尸吧。

  

  后续估计还得增加一下自动发现功能,毕竟不能老是自己去修改配置文件来增加phantomjs服务器的数量。。。

  PS:

    苦逼爬虫。。  为了提高效率忙活了两天,结果还好,成功提升了将近10倍效率 :)

    说一下服务器配置:

      腾讯云  8核 32G内存 20M带宽 普通云主机

      然后我启动了12个phantomjs容器。。。

      然后服务器负载在14、15、16左右,虽然挺高的,但还好,能用了

  如果有大神有更好的办法,欢迎交流

  参考:

    HAproxy指南之haproxy配置详解1(理论篇)

    关于haproxy负载均衡的算法整理

    负载均衡软件HAProxy有哪些优点?

    项目详解4—haproxy 反向代理负载均衡

    HAProxy, session sticky and balance algorithm

    HOW TO LOAD BALANCE AN HTTP SERVER (USING WITH HAPROXY OR POUND)

    Compose file version 2 reference  

  转载请注明来源。。。  我咋这么自觉呢

原文地址:https://www.cnblogs.com/dyfblog/p/8818811.html

时间: 2024-10-07 19:32:25

docker+phantomjs+haproxy 搭建phantomjs集群的相关文章

Haproxy搭建web集群

Haproxy搭建web集群重点内容1:1.四层负载均衡:1)DNS轮询:将同一个域名解析为多个不同的ip地址实现负载均衡.2)Nginx负载均衡:通过定义upstream 组名 {server ip:port weight=权重;-}后端服务,然后通过proxy_pass http://组名实现负载均衡.3)LVS负载均衡:通过ipvsadm定义VIP(集群IP)和real server(后端服务器)调用linux内核(kernel)模块ip_vs实现负载均衡.2.haproxy负载均衡:通过

docker容器中搭建kafka集群环境

Kafka集群管理.状态保存是通过zookeeper实现,所以先要搭建zookeeper集群 zookeeper集群搭建 一.软件环境: zookeeper集群需要超过半数的的node存活才能对外服务,所以服务器的数量应该是2*N+1,这里使用3台node进行搭建zookeeper集群. 1. 3台linux服务器都使用docker容器创建,ip地址分别为 NodeA:172.17.0.10 NodeB:172.17.0.11 NodeC:172.17.0.12 2. zookeeper的doc

linux服务器 Haproxy搭建Web集群环境实例

操作系统:CentOS 6.5   Haproxy软件版本:haproxy-1.4.24  Nginx软件版本:nginx-1.6.2 Haproxy是目前比较流行的一种集群调度工具,之前提到Nginx的upstream模块也能实现集群的负载均衡,但是Nginx不能对节点进行健康检查,性能也没有Haproxy好 负载均衡常用的调度算法:RR(Round Robin)轮询调度.LC(Least Connections)最小连接数和SH(Source Hashing)基于来源的访问调度 案例拓扑图:

Zookeeper:Docker环境下搭建Zookeeper集群

下载镜像 docker pull garland/zookeeper 搭建: docker run -d --name=zk1 --net=host -e SERVER_ID=1 -e ADDITIONAL_ZOOKEEPER_1=server.1=localhost:2888:3888 -e ADDITIONAL_ZOOKEEPER_2=server.2=localhost:2889:3889 -e ADDITIONAL_ZOOKEEPER_3=server.3=localhost:2890:

RabbitMQ:Docker环境下搭建rabbitmq集群

RabbitMQ作为专业级消息队列:如何在微服务框架下搭建 使用组件 文档: https://github.com/bijukunjummen/docker-rabbitmq-cluster 下载镜像: git clone https://github.com/bijukunjummen/docker-rabbitmq-cluster.git cd docker-rabbitmq-cluster/clusterdocker-compose up -d 等待下载完成: 会自动构建3个rabbitM

使用Haproxy搭建web集群

环境: 代理haproxy:192.168.100.155 后台nginx:192.168.100.153-154 1.安装nginx服务器:(192.168.100.153) yum -y install pcre-devel zlib-devel wgetftp://ftp.linuxfan.cn/tools/nginx-1.6.0.tar.gz tar zxvf nginx-1.6.0.tar.gz -C /usr/src/ cd /usr/src/nginx-1.6.0 useradd

docker搭建Hadoop集群

一个分布式系统基础架构,由Apache基金会所开发. 用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力高速运算和存储. 首先搭建Docker环境,Docker版本大于1.3.2 安装主机监控程序和加速器(curl -sSL https://get.daocloud.io/daomonit/install.sh | sh -s 7a029f60d36056fe1b85fabca6a133887245abe6) docker pull daocloud.io/library

用Docker在一台笔记本电脑上搭建一个具有10个节点7种角色的Hadoop集群(下)-搭建Hadoop集群

上篇:用Docker在一台笔记本电脑上搭建一个具有10个节点7种角色的Hadoop集群(上)-快速上手Docker 上篇介绍了快速上手Docker部分,下面接着介绍搭建Hadoop集群部分. 六.搭建Hadoop伪分布模式 我们先用前面创建的这个容器来搭建Hadoop伪分布模式做测试,测试成功后再搭建完全分布式集群. 1.SSH这个centos容器可以看做是一个非常精简的系统,很多功能没有,需要自己安装.Hadoop需要SSH,但容器没有自带,需要我们安装.①安装SSH # yum -y ins

docker容器搭建kafka集群

Docker搭建kafka集群 ?  需求说明: 公司目前有三个环境,生产环境,测试和演示环境,再包括开发人员还有开发的环境,服务器上造成了一定的资源浪费,因为环境需要依赖zookeeper和kafka,redis这些服务,只要搭一个环境,所有东西都要重新搭一遍,所以搭建kafka集群,让大部分环境都连接一个集群,把单个的服务变成公共的,稳定并易于管理 ?  Kafka集群管理和状态保存是通过zookeeper来实现的,要先部署zk集群 ?  环境说明: centos系统安装docker,通过d