Eureka简介

Eureka与覆盖网络

Eureka在IP网络的基础上提供了覆盖网络(Overlay Network),所谓覆盖网络,实际上就是在网络上的网络,比如VoIP,DNS,DHT,VxN协议甚至热炒的区块链,它们的特点就是节点间通信与下层IP协议解藕(比如地址,端口,协议等): A用户给B用户打电话,只需要知道对方的电话号码,而不需要对方的IP地址,这里的“电话号码”就可以看作一个NodeId,而电话连线的过程对用户是透明的。

在Eureka中,通过VIP(Virtual IP,同义词,非真正的VIP协议)来表示一个节点集群的ID,节点间通信只需要考虑VIP,而不用考虑其下层网络的属性(是否可用,IP是否又变了等)。

Eureka的数据结构

Eureka实际就是维护了一个远程中心化的Map,key为Virtual IP,也就是Application的名称,Value为可用服务实例列表(Instance)

Map<String, List<ServiceInstance>>

它的函数原型大致如下

void put(KEY vip, List<ServiceInstance> services);  // 发送实例到中心服务器
List<ServiceInstance> get(KEY vip);  // 通过VIP获取实例

因此,当你看到“云”,“微服务”时,千万不要产生胆怯心理,也不要被忽悠了。Eureka相比于Dubbo等RPC框架简单了许多,更比通信领域简单了太多。普通开发可以在一下午上手,中级开发看完本书后即可了解70%的流程。

与其他组件的对比

从客户端的角度

如果说让我来做个类比的话,那么DNS中的特例“HTTP DNS”就是Eureka最好的类比。HTTP DNS在Android等客户端中广泛使用,客户端向DNS发送域名,服务端返回了一串解析后的IP列表

# 下为OpenDNS的例子
$ curl http://119.29.29.29/d?dn=gitbook.com
104.25.212.20;104.25.213.20                                                

而Eureka也一样,通过向EurekaServer发送VIP(Virtual IP,你可以把它看作内网域名)名称,也返回一串地址,Java代码中对应的请求是

# api-prod-sz1就是vip,它对应了很多Server实例
$ curl localhost:8761/eureka/vips/api-prod-sz1

返回了如下(以XML为例)

<applications>
    <versions__delta>-1</versions__delta>
    <apps__hashcode>UP_1_</apps__hashcode>
    <application>
        <name>API-PROD-SZ1</name>
        <instance>
            <instanceId>10.0.0.4:api-prod-sz1</instanceId>
            <hostName>10.0.0.4</hostName>
            <app>API-PROD-SZ1</app>
            <ipAddr>10.0.0.4</ipAddr>
            <status>UP</status>
            <overriddenstatus>UNKNOWN</overriddenstatus>
            <port enabled="true">8080</port>
            <securePort enabled="false">443</securePort>
            <countryId>1</countryId>
            <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
                <name>MyOwn</name>
            </dataCenterInfo>
            <leaseInfo>
                ...
            </leaseInfo>
            <metadata>
                <management.port>8080</management.port>
                <jmx.port>53894</jmx.port>
            </metadata>
            ...
            <vipAddress>api-prod-sz1</vipAddress>
        </instance>
    </application>
</applications>

因此从客户端黑盒的角度来看,Eureka与HTTP DNS没有很大的差别

EurekaHTTP DNS
实例粒度节点级节点级
侵入式有,也可以使用RESTfulAPI
短板老项目不好改需要跨防火墙,维护成本高,背后隐藏着Nginx(upsource等)
控制域与命名服务直连与命名服务直连
用户域P2PP2P
传输语法HTTPHTTP
抽象语法按照Wiki的几个API来搞发送一个QueryString即可

所以千万不要畏惧Eureka这样的新技术,它们的使用并不需要非常高的编码水平。

网上有很多文章拿Eureka与Nginx进行对比,我认为这个是不公平的,Nginx本身定位是位于防火墙前面向客户的,而Eureka用于内部PRC的。对于我个人来说,Nginx/DNS这一套折腾下来最麻烦的就是防火墙的各种开通,需要各种跳板去调试配置,分析定位速度非常慢。

从CAP的角度

根据CAP定律,C——数据一致性,A——服务可用性,P——服务对网络分区故障的容错性,这三个无法同时满足,因此各个微服务框架均侧重选了2个

ZookeeperEureka
C(Consistence)YN
A(Avaliabilty)NY

上面说的只是理论问题,实际上Zookeeper在真正生产使用时,稍微请求多一点就跪了,雪崩(连接数太多)直接导致所有服务无法使用,比如在生产环境用遇到过

  • 断电(没想到吧) -> Zk持久化EOF错误了 -> 无法启动 -> 负载压到少数机器 -> 慢慢变慢最终爆掉
  • 负载过多 -> 连接数下不来 -> 线程卡在Netty的NIO等待,没法更新树->最终爆掉

此外还有Zk难以抓包定位,无可视化界面等问题,这些都需要自己维护定制工具

与Dubbo的关系

  • 正如上面的Curl例子所示,Eureka的命名服务主要是通过VIP帮你找到IP,不支持方法级服务的注册发现,因此比基于zk的Dubbo更简化(但是可以用Swagger+Feign来实现)。
  • Eureka的增量更新不是原子操作,但是保证最终一致性
  • 基于Eureka的RPC一般是HTTP调用,也就是服务端开一个RestController就可以了,比Dubbo等微服务框架侵入性更低

与SpringCloudFunction的关系

Eureka与Spring Cloud Function(FAAS)没有任何关系。Spring的FAAS开源实现目前还不成熟,内部直接用文件做的全局共享,不存在分布式特性

Eureka的主要概念

在Eureka中,Netflix基于AWS设计出了很多Java类

  • EurekaServer: 服务注册发现的实现,你可以把它看作Zookeeper, etcd等

  • EurekaClient: 一个对Eureka的RESTful规范的实现,心跳,注册等流程均通过这里以HTTP请求的形式发出,你暂时可以把它理解为轮询HTTP请求发生器。

  • VIP(Virtual IP): 虚拟IP,命名服务中的Key,类似于域名

  • Feign: 注解形式的HTTP客户端,API基本与Retrofit一致,内部通过动态代理拼装HTTP请求,我之前也写Retrofit的文章,这里就多不介绍了。

  • SpringCloud-xxx: 对上面的包装,重点在于它的AutoConfiguration帮你干了很多重复工作,此外Maven的依赖问题也帮你解决了(老项目的工作量就在这了)