Consul + fabio 实现自动服务发现、负载均衡

Consul + fabio 实现自动服务发现、负载均衡目录 ConsulFabio 服务发现的特点工作原理 Demo 结合 kubernetes 扩容 nbsp Consulhashic 团队开发就是大名鼎鼎开发 vagrant 的团队 Consul nbsp 是一个提供服务发现 健康检测 K V 存储支持分布式高可用多数据中心的服务软件 比较类似 ZooKeeper 但又比它多了一些功能 具体可以参考 nbsp Consul 和 ZooKeeper 的区别

目录

Consul

Fabio

服务发现的特点

工作原理

Demo

结合kubernetes扩容

 

Consul


hashicorp团队开发 就是大名鼎鼎开发 vagrant 的团队。
Consul 是一个提供服务发现、健康检测、K/V存储支持分布式高可用多数据中心的服务软件。
比较类似ZooKeeper但又比它多了一些功能。 具体可以参考 Consul和ZooKeeper的区别




 

Fabio


fabio 是 ebay 团队用 golang 开发的一个快速、简单零配置,能够让 consul 部署的应用快速支持 http(s) 的负载均衡路由器。
因为 consul 支持服务注册与健康检查,所以 fabio 能够零配置提供负载,升级部署从未如此简单。
根据项目的介绍,fabio 能提供每秒15000次请求。
有了这两个组件非常容易做服务发现与自动负载均衡, “神器在手、天下我有!” ^ _ ^






 

服务发现的特点


服务与服务之间的调用通常需要在配置文件中填写好主机和端口,但是这样不易于维护,且分布式环境中不易于部署与扩容。
那么此时就需要考虑服务启动的时候自己把主机和端口以及一些其他信息注册到注册中心,这样其他服务可以从中找到它。
甚至更为简单的,注册完毕后通过 DNS 的方式来『寻址』。比如 Zookeepr 可以很好的完成这个工作,但是其中还有一个弊端就是服务的健康检查,服务注册到注册中心之后如何保证这个服务一定可用?此时就需要自己来写逻辑,当服务不可用的时候自动从注册中心下线。 Consul 可以很轻易的解决这个问题。




 

工作原理


Consul 提供了一套健康检测机制,简单的说针对 http 类型的服务(consul 也支持其他类型例如tcp)在注册的时候可以顺便注册下健康检测的信息,提供一个健康检测的地址(url)以及一个频率超时时间,这样的话 consul 会定期的发送这个url请求,当状态码是200的时候设置此服务是健康的状态,否则是故障状态。
既然注册到consul的服务能够自己维护健康状态,此时 fabio 的工作就很简单了! 就是直接从consul 注册表里面取出健康的服务,根据服务注册时候的 tags 配置自动创建自己的路由表,然后当一个 http 请求过来的时候自动去做负载均衡

简单的流程图如下:






 

Demo


这里我们开始写一个 demo 服务来体验一波 consul+ fabio, 顺便用 docker + k8s 来编排扩容。
因为 consul + fabio 都支持 docker 的方式运行,这里均以 docker 方式为例子。

docker pull magiconair/fabio  docker pull consul

consul 可以用集群的方式开发环境中也可以用 dev 模式 可以参考这里
这里可以参考我整理的单机部署 consul 集群的 docker compose 配置:

version: '2' services: consul_server_1: image: "consul:latest" container_name: "consul_server_1" environment:   CONSUL_LOCAL_CONFIG: '{"leave_on_terminate": true}' networks:   app_net:     ipv4_address: 172.17.0.3 command: "agent -server -bind=172.17.0.3 -client=172.17.0.3 -retry-join=172.17.0.2" consul_server_2: image: "consul:latest" container_name: "consul_server_2" ports:   - "8600:8600"   - "8500:8500" networks:   app_net:     ipv4_address: 172.17.0.4 command: "agent -server -bind=172.17.0.4 -client=172.17.0.4 -retry-join=172.17.0.3 -ui" consul_server_3: image: "consul:latest" container_name: "consul_server_3" environment:   CONSUL_LOCAL_CONFIG: '{"leave_on_terminate": true}' networks:   app_net:     ipv4_address: 172.17.0.5 command: "agent -server -bind=172.17.0.5 -client=172.17.0.5 -retry-join=172.17.0.4 -bootstrap-expect=3" networks: app_net: driver: bridge ipam:   config:   - subnet: 172.17.0.0/24 

consul-ui.png

fabio: image: "magiconair/fabio" ports: - "9998:9998" - "9999:9999" volumes: - ./fabio.properties:/etc/fabio/fabio.properties 

fabio 虽然说是零配置但是某些情况下还是需要个性化配置一些东西,此时可以去写一个简单的配置fabio.properties
指定了 consul 的地址端口以及自身的一些统计信息等等

registry.consul.register.addr = 172.16.0.21:9998 registry.consul.addr = 172.16.0.21:8500 metrics.target = stdout 

 

fabio.png

route add  
       
           
        
            
         
            weight  
          
             tags " 
           
             , 
            
              ,..." - Add route for service svc from src to dst and assign weight and tags route add  
             
                 
              
                  
               
                  weight  
                
                  - Add route for service svc from src to dst and assign weight 
                 
                
               
              
             
            
           
          
         
       
package main import ( "flag" "fmt" "log" "net" "net/http" "os" "os/signal" "path/filepath" "strconv" "strings" "github.com/magiconair/fabio-example/_third_party/github.com/hashicorp/consul/api" ) func main() { var addr, name, prefix string flag.StringVar(&addr, "addr", "127.0.0.1:5000", "host:port of the service") flag.StringVar(&name, "name", filepath.Base(os.Args[0]), "name of the service") flag.StringVar(&prefix, "prefix", "", "comma-sep list of host/path prefixes to register") flag.Parse() if prefix == "" {     flag.Usage()     os.Exit(1) } // register prefixes prefixes := strings.Split(prefix, ",") for _, p := range prefixes {     http.HandleFunc(p, func(w http.ResponseWriter, r *http.Request) {         fmt.Fprintf(w, "Serving %s from %s on %s\n", r.RequestURI, name, addr)     }) } // start http server go func() {     log.Printf("Listening on %s serving %s", addr, prefix)     if err := http.ListenAndServe(addr, nil); err != nil {         log.Fatal(err)     } }() // register consul health check endpoint http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {     fmt.Fprintln(w, "OK") }) // build urlprefix-host/path tag list // e.g. urlprefix-/foo, urlprefix-/bar, ... var tags []string for _, p := range prefixes {     tags = append(tags, "urlprefix-"+p) } // get host and port as string/int host, portstr, err := net.SplitHostPort(addr) if err != nil {     log.Fatal(err) } port, err := strconv.Atoi(portstr) if err != nil {     log.Fatal(err) } // register service with health check serviceID := name + "-" + addr service := &api.AgentServiceRegistration{     ID:      serviceID,     Name:    name,     Port:    port,     Address: host,     Tags:    tags,     Check: &api.AgentServiceCheck{         HTTP:     "http://" + addr + "/health",         Interval: "1s",         Timeout:  "1s",     }, } client, err := api.NewClient(api.DefaultConfig()) if err != nil {     log.Fatal(err) } if err := client.Agent().ServiceRegister(service); err != nil {     log.Fatal(err) } log.Printf("Registered service %q in consul with tags %q", name, strings.Join(tags, ",")) // run until we get a signal quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt, os.Kill) <-quit // deregister service if err := client.Agent().ServiceDeregister(serviceID); err != nil {     log.Fatal(err) } log.Printf("Deregistered service %q in consul", name) }   
CONSUL_HTTP_ADDR=172.16.0.21:8500 ./fabio-example -addr=172.16.0.17:9876 -prefix=a.com/ 
#    Service Host    Path    Dest    Weight 1   fabio-example   a.com   /   http://172.16.0.17:9876/    100% 

 

➜  ~ curl -iv -H 'Host: a.com' 172.16.0.21:9999/ *   Trying 172.16.0.21... * Connected to 172.16.0.21 (172.16.0.21) port 9999 (#0) > GET / HTTP/1.1 > Host: a.com > User-Agent: curl/7.43.0 > Accept: */* > < HTTP/1.1 200 OK HTTP/1.1 200 OK < Content-Length: 49 Content-Length: 49 < Content-Type: text/plain; charset=utf-8 Content-Type: text/plain; charset=utf-8 < Date: Fri, 22 Jul 2016 01:01:28 GMT Date: Fri, 22 Jul 2016 01:01:28 GMT < Serving / from fabio-example on 172.16.0.17:9876 * Connection #0 to host 172.16.0.21 left intact 

 

结合kubernetes扩容


如果基于 k8s 做编排的话需要做一些修改,例如: ip端口 要动态的从容器里面获取,服务启动的时候那个 tags prefix 需要配置,可以参考fabio的文档
这里我就用 java 快速搭建了一个, Spring cloud框架提供了服务发现一条龙服务,针对 consul 的就是 spring-cloud-consul ,只要一行代码 几行配置就能快速使用 :)
Application类上添加@EnableDiscoveryClient注解。

application.yml








spring: cloud: consul:   discovery:     healthCheckPath: ${management.contextPath}/health  #健康检测的路径      healthCheckInterval: 15s #健康检测的频率     tags: urlprefix-api.xxxx.com/  #fabio 的路由规则 
$ kubectl scale --replicas=10 rc api $ kubectl get pods [root@172-16-0-17 fabio-example]# kubectl get pods NAME                                             READY     STATUS    RESTARTS   AGE api-6xytx                                         1/1       Running   0          11s api-9eaae036e2dce379-ac30s        1/1       Running   0          14h api-dfmtv                                         1/1       Running   0          11s api-eo01h                                         1/1       Running   0          11s api-hn1kv                                         1/1       Running   0          11s api-iyqmg                                         1/1       Running   0          11s api-k32ud                                         1/1       Running   0          11s api-q10a7                                         1/1       Running   0          11s api-re7e1                                         1/1       Running   0          11s api-tm2pk                                         1/1       Running   0          11s 

docker-consul.png

#    Service Host    Path    Dest                Weight 1   api api.com     /   http://172.31.9.3:8080/     10% 2   api api.com     /   http://172.31.9.2:8080/     10% 3   api api.com     /   http://172.31.82.6:8080/    10% 4   api api.com     /   http://172.31.82.4:8080/    10% 5   api api.com     /   http://172.31.28.3:8080/    10% 6   api api.com     /   http://172.31.28.2:8080/    10% 7   api api.com     /   http://172.31.23.7:8080/    10% 8   api api.com     /   http://172.31.23.2:8080/    10% 9   api api.com     /   http://172.31.12.6:8080/    10% 10  api api.com     /   http://172.31.12.4:8080/    10%
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210346.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 上午7:46
下一篇 2026年3月19日 上午7:47


相关推荐

  • 嵌入式Ubuntu 搭建caffee环境

    嵌入式Ubuntu 搭建caffee环境1 首先登录 caffee 官网 转到 github 点击 Installation 选择 ubuntu 环境 2 小机使用的是 ubuntu16 04 所以按照指令搭建 3 首先安装如下安装包 sudoapt getinstall nbsp libprotobuf dev nbsp libleveldb dev nbsp libsnappy dev nbsp libopencv dev

    2026年3月17日
    3
  • ggplot2数据分析与图形艺术_plot画多条曲线

    ggplot2数据分析与图形艺术_plot画多条曲线接着我们之前复现过的一篇NC文章(复现《naturecommunications》散点小提琴图+蜜蜂图),有一张关于差异蛋白的火山图,但是不同的是他的阈值设定不是我们普通的横向纵向,而是曲线阈值!image.png本来我以为这是一个个例,本篇文章作者博眼球的做法,但是检索了一下发现我付肤浅了,有很多文章,但是有一个特点,双曲线阈值应用在蛋白组差异基因的筛选上,这样的方式类似与“软阈值”吧,能够找到更显著的蛋白,值得在自己的研究中使用。image.png(Reference:ProteomicsofMe

    2026年4月13日
    5
  • 淘宝刷流量软软件,互刷点击量,直通车点击系统【胖虎图图-互点专家】

    淘宝刷流量软软件,互刷点击量,直通车点击系统【胖虎图图-互点专家】软件绿色免安装,打开即可使用。软件大小:1.4MB支持平台:win2000/win2003/winxp/win7/win8下载地址:http://www.phtoto.com/download/胖虎图图-互动点击.rar【2013-8-12】更新日志:修复无法停止任务的bug; 【2013-8-11】更新日志:新增支持谷歌搜索;新增支持360搜索;新增支持搜

    2026年4月15日
    6
  • 非零矩阵A可以写成某个列满秩矩阵与某个行满秩矩阵的乘积

    非零矩阵A可以写成某个列满秩矩阵与某个行满秩矩阵的乘积非零矩阵 A 可以写成某个列满秩矩阵与某个行满秩矩阵的乘积引理 设 AAA 是 m rm timesrm r 矩阵 则 AAA 是列满秩的充要条件为存在 m mm timesmm m 可逆矩阵 PPP 使 A P ErO A P begin pmatrix E r O end pmatrix A P Er O 同样地 AAA 为行满秩的充要条件为存在 r rr timesrr r 的可逆矩阵 Q 使 A Q EmO A Q begin pmatrix E m amp O end pmatr

    2026年3月16日
    2
  • OpenClaw Skill 与 OpenAI Function Calling 深度对比:一文看懂本质差异

    OpenClaw Skill 与 OpenAI Function Calling 深度对比:一文看懂本质差异

    2026年3月13日
    1
  • layuiAdmin后台框架以及动态权限

    layuiAdmin后台框架以及动态权限alyuiAdmin 首页加载权限 layuimini init api init json 原项目请求的是这些静态资源 layuimini init permission initMenu init json 格式所以要从后台定义请求这些数据特别注意 一定要按照要求格式 采用递归 由上到下获取数据

    2026年3月16日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号