精华内容
下载资源
问答
  • 背景本文并不介绍服务发现的基本原理。除了一致性算法之外,其他并没有太多高深的算法,网上的资料很容易让大家明白上面是服务发现。想直接查看结论的同学,请直接跳到文末。目前,市面上有非常多的服务发现工具,...

    背景


    本文并不介绍服务发现的基本原理。除了一致性算法之外,其他并没有太多高深的算法,网上的资料很容易让大家明白上面是服务发现。


    想直接查看结论的同学,请直接跳到文末。


    目前,市面上有非常多的服务发现工具,《Open-Source Service Discovery》(http://jasonwilder.com/blog/2014/02/04/service-discovery-in-the-cloud/)一文中列举了如下开源的服务发现工具。


    NameTypeAP or CPLanguageDependenciesIntegration
    ZookeeperGeneralCPJavaJVMClient Binding
    DoozerGeneralCPGo
    Client Binding
    EtcdGeneralMixed (1)Go
    Client Binding/HTTP
    SmartStackDedicatedAPRubyhaproxy/ZookeeperSidekick (nerve/synapse)
    EurekaDedicatedAPJavaJVMJava Client
    NSQ (lookupd)DedicatedAPGo
    Client Binding
    SerfDedicatedAPGo
    Local CLI
    Spotify (DNS)DedicatedAPN/ABindDNS Library
    SkyDNSDedicatedMixed (2)Go
    HTTP/DNS Library


    (1) If using the consistent parameter, inconsistent reads are possible
    (2) If using a caching DNS client in front of SkyDNS, reads could be inconsistent


    上面表格中,前三个是通用的,后面都是各大公司自己造的轮子,应用范围并不广,我也就不深入研究了。


    此外,这篇文章是14年写的,当时它并没有研究Consul,放到表格中,Consul则应该是General、CP、Go、No dependency、Http/DNS Library。


    截止到今天,除了容器编排框架k8s、istio/envoy自己实现了服务发现机制(他们也兼容第三方的服务发现工具),似乎也没有其他的知名的服务发现框架出现了。


    下面我就zookeeper、etcd、consul这三款进行下比较。


    比较


    ZooKeeper


    ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable. Because of the difficulty of implementing these kinds of services, applications initially usually skimp on them ,which make them brittle in the presence of change and difficult to manage. Even when done correctly, different implementations of these services lead to management complexity when the applications are deployed.



    官网这么介绍ZooKeeper的,翻译过来,ZooKeeper的功能有:


    1. 作为配置信息的存储的中心服务器

    2. 命名服务

    3. 分布式同步

    4. 分组服务


    能看出,ZooKeeper并不只是作为服务发现框架使用的,它非常庞大。


    如果只是打算将ZooKeeper作为服务发现工具,就需要用到其配置存储和分布式同步的功能。前者可以理解成具有一致性的KV存储,后者提供了ZooKeeper特有的watcher注册于异步通知机制,ZooKeeper能将节点的状态实时异步通知给ZooKeeper客户端。


    ZooKeeper使用


    ZooKeeper的使用流程如下:


    1. 确保有所选语言的sdk,理论上github上第三方的库有一些,仔细筛选一下应该可以用。

    2. 调用zookeeper接口连接zookeeper服务器。

    3. 注册自身服务

    4. 通过watcher获取监听服务的状态

    5. 服务提供者需自行保持与zookeeper服务器的心跳。


    《Zookeeper C API 指南》(http://www.cnblogs.com/haippy/archive/2013/02/21/2920280.html)写了八篇文章介绍了如何使用ZooKeeper的C语言API。


    总得来说,ZooKeeper需要胖客户端,每个客户端都需要通过其SDK与ZooKeeper服务保活,增加了编写程序的复杂性。此外,还提供api实现服务注册与发现逻辑,需要服务的消费者实现服务提供者存活的检测。


    etcd


    etcd是一个采用http协议的分布式键值对存储系统,因其易用,简单。很多系统都采用或支持etcd作为服务发现的一部分,比如kubernetes。但正事因为其只是一个存储系统,如果想要提供完整的服务发现功能,必须搭配一些第三方的工具。


    比如配合etcd、Registrator、confd组合,就能搭建一个非常简单而强大的服务发现框架。但这种搭建操作就稍微麻烦了点,尤其是相对consul来说。所以etcd大部分场景都是被用来做kv存储,比如kubernetes。


    consul


    相较于etcd、zookeeper,consul最大的特点就是:它整合了用户服务发现普遍的需求,开箱即用,降低了使用的门槛,并不需要任何第三方的工具。代码实现上也足够简单。


    Consul has multiple components, but as a whole, it is a tool for discovering and configuring services in your infrastructure. It provides several key features:


    1. Service Discovery

    2. Health Checking

    3. KV Store

    4. Multi Datacenter


    展开了说,consul的功能有:


    1. 通过DNS或HTTP,应用能轻易地找到它们依赖的系统

    2. 提供了多种健康检查方式:http返回码200,内存是否超限,tcp连接是否成功

    3. KV存储,并提供http api

    4. 多数据中心,这点是zookeeper所不具备的。


    consul使用


    相比于zookeeper的服务发现使用,consul并不需要专门的sdk集成到服务中,因此它不限制任何语言的使用。我们看看consul一般是怎么使用的。


    1. 每台服务器上都要安装一个consul agent。

    2. consul agent支持通过配置文件注册服务,或者在服务中通过http接口来注册服务。

    3. 注册服务后,consul agent通过指定的健康检查方式,定期检查服务是否存活。

    4. 如果服务想查询其他服务的存活状态,只需要与本机的consul agent发起一次http请求或者dns请求即可。


    简单点说,consul的使用不依赖任何sdk,依靠简单的http请求就能满足服务发现的所有逻辑。


    不过,服务每次都从consul agent获取其他服务的存活状态,相比于zookeeper的watcher机制,实时性稍差一点,需考虑如何尽可能提高实时性,问题不会很大。


    总结


    名称优点缺点接口一致性算法
    zookeeper1.功能强大,不仅仅只是服务发现
    2.提供watcher机制能实时获取服务提供者的状态
    3.dubbo等框架支持
    1.没有健康检查
    2.需在服务中集成sdk,复杂度高
    3.不支持多数据中心
    sdkPaxos
    consul1.简单易用,不需要集成sdk
    2.自带健康检查
    3.支持多数据中心
    4.提供web管理界面
    1.不能实时获取服务信息的变化通知http/dnsRaft
    etcd1.简单易用,不需要集成sdk
    2.可配置性强
    1.没有健康检查
    2.需配合第三方工具一起完成服务发现
    3.不支持多数据中心
    httpRaft


    为了以后支持多数据中心,同时为了快速支持不同的语言比如nodejs、python服务,我会选择consul作为我们的服务发现框架,但是实时获取服务信息变化通知的问题需尽可能减小。


    参考文献:


    • Consul vs. Other Software(https://www.consul.io/intro/vs/index.html

    • 服务发现:Zookeeper vs etcd vs Consul(http://dockone.io/article/667

    • Consul vs 其他软件(https://skyao.gitbooks.io/learning-consul/content/introduction/consul_vs_other_software.html

    • Comparing ZooKeeper and Consul(https://www.slideshare.net/IvanGlushkov/zookeeper-vs-consul-41882991


    出处:http://www.servercoder.com/2018/03/30/consul-vs-zookeeper-etcd/


    版权申明:内容来源网络,版权归原创者所有。除非无法确认,我们都会标明作者及出处,如有侵权烦请告知,我们会立即删除并表示歉意。谢谢。



    架构文摘

    ID:ArchDigest

    互联网应用架构丨架构技术丨大型网站丨大数据丨机器学习

    更多精彩文章,请点击下方:阅读原文

    展开全文
  • 一款集服务发现、rpc通信于一体的框架,通信层基于netty实现,底层原理基于反射、动态代理实现。 纯基于Javaconfig方式配置,无xml。配置中心基于redis。 特性 传输方式:TCP长连接,NIO异步通信 序列化:json ...

    背景及简介

            在微服务架构大行其道的今天,服务发现及rpc通信框架扮演的“戏份”也越来越重,业界使用较广泛的有阿里系的dubbo,或者为中小型项目而生的spring cloud全家桶等等,都得到了实践与认可。大部分初学者的学习往往只能停留在使用层面,为了更深入的了解服务发现机制以及rpc的原理,后续的文章《徒手编写服务发现框架》系列,我们一起揭开它的神秘面纱,自己动手实现一款简单的服务发现框架!

           我们目标是要编写一款集服务发现、rpc通信于一体的框架,通信层基于netty实现,底层原理基于反射、动态代理实现。 纯基于Javaconfig方式配置,无xml。配置中心基于redis。

    特性

    • 传输方式:TCP长连接,NIO异步通信
    • 序列化:json
    • 适用场景:服务间高性能、低延迟调用

    注意事项

    • 注册中心目前采用redis实现,注册中心可用性暂不保证

    进阶篇(待实现)

    • 心跳检测
    • 负载均衡
    • 链路监控
    • 熔断

    服务提供端配置:

    @Configuration 
     public class RpcConfig { 
         @Bean
         public RegistryConfig registryConfig(){
             RegistryConfig registryConfig = new RegistryConfig();
             registryConfig.setRegistryHost("127.0.0.1");
             registryConfig.setRegistryPort(6379);
             registryConfig.setPort(3333);
             registryConfig.setServerName("spring-boot-demo");
             registryConfig.setProviderConfigs(providerConfig());
             return registryConfig;
         }
    
         private Set<ProviderConfig> providerConfig(){
             Set<ProviderConfig> providerConfigs = new HashSet<>();
             ProviderConfig<IUserServiceApiImpl> providerConfig = new ProviderConfig<>();
             providerConfig.setApiName("com.zxm.rpc.api.IUserServiceApi");
             providerConfig.setImplementsClass(IUserServiceApiImpl.class);
             providerConfigs.add(providerConfig);
             return providerConfigs;
         }
    
         @Bean
         public RegistryHandler registryHandler() throws UnknownHostException{
             RegistryHandler registryHandler = new RegistryHandler(registryConfig());
             registryHandler.registry();
             return registryHandler;
         }
    
         @Bean
         public ProviderProxyInvoker providerProxyInvoker()throws UnknownHostException{
             return new ProviderProxyInvoker(registryConfig(),registryHandler());
         }
     }

     

    服务消费端配置:

    @Configuration
    public class RefConfig {
        @Bean
        public IUserServiceApi referenceConfig() {
            ReferenceConfig<IUserServiceApi> referenceConfig = ReferenceConfig
                    .instance()
                    .apiName("com.zxm.rpc.api.IUserServiceApi")
                    .providerName("spring-boot-demo")
                    .registryHost("10.13.1.210")
                    .registryPort(6379)
                    .timeout(5000)
                    .build();
            return referenceConfig.register();
        }
    }

    项目地址: https://github.com/zhangxiaomin1993/rpc-server-sdk

    声明:文章和项目不以商业盈利为目的,仅作为本人技术积累的沉淀,分享给大家,有兴趣的朋友欢迎访问交流,共同学习和进步!大佬和专家路过,不喜勿喷!

    展开全文
  • 服务发现框架中,服务提供端的作用是为消费端提供接口调用,提供端应该首先启动,依次完成服务注册、接口暴露等等。 首先看下提供端的配置信息: /** * @Author zxm * @Description * @Date Create in 下午 ...

    上一篇文章我们简单介绍了项目的背景及基本组件情况,这篇文章我们主要分享一下服务提供端的设计实现。在服务发现框架中,服务提供端的作用是为消费端提供接口调用,提供端应该首先启动,依次完成服务注册、接口暴露等等。

    首先看下提供端的配置信息:

    /**
     * @Author zxm
     * @Description
     * @Date Create in 下午 4:33 2019/1/22 0022
     */
    public class RegistryConfig {
        /**
         * 注册中心host
         */
        private String registryHost;
    
        /**
         * 注册中心端口
         */
        private Integer registryPort;
    
        /**
         * 应用名称
         */
        private String serverName;
    
        /**
         * 服务提供方端口
         */
        private Integer port;
    
        /**
         * 服务提供接口集合
         */
        private Set<ProviderConfig> providerConfigs;
    
        public String getRegistryHost() {
            return registryHost;
        }
    
        public void setRegistryHost(String registryHost) {
            this.registryHost = registryHost;
        }
    
        public Integer getRegistryPort() {
            return registryPort;
        }
    
        public void setRegistryPort(Integer registryPort) {
            this.registryPort = registryPort;
        }
    
        public String getServerName() {
            return serverName;
        }
    
        public void setServerName(String serverName) {
            this.serverName = serverName;
        }
    
        public Integer getPort() {
            return port;
        }
    
        public void setPort(Integer port) {
            this.port = port;
        }
    
        public Set<ProviderConfig> getProviderConfigs() {
            return providerConfigs;
        }
    
        public void setProviderConfigs(Set<ProviderConfig> providerConfigs) {
            this.providerConfigs = providerConfigs;
        }
    
        public String getLocalHostAddrss(){
            try {
                return InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                return null;
            }
        }
    }

    RegistryConfig 为注册业务相关的配置信息,主要需要配置注册中心的ip和端口、服务的名称、接口提供方的端口以及暴漏的接口信息等等。

    /**
     * @Author zxm
     * @Description
     * @Date Create in 上午 10:47 2019/1/23 0023
     */
    public class ProviderConfig<T>{
        /**
         * 接口全称
         */
        private String apiName;
    
        /**
         * 接口实现类
         */
        private Class<T> implementsClass;
    
        public String getApiName() {
            return apiName;
        }
    
        public void setApiName(String apiName) {
            this.apiName = apiName;
        }
    
        public Class<T> getImplementsClass() {
            return implementsClass;
        }
    
        public void setImplementsClass(Class<T> implementsClass) {
            this.implementsClass = implementsClass;
        }
    }

    ProviderConfig是提供方对外暴露的接口的配置信息,包括接口的全限定名和实现类信息,用于通信过程中被反射调用。

    注册处理器

    /**
     * @Author zxm
     * @Description
     * @Date Create in 下午 4:48 2019/1/22 0022
     */
    public class RegistryHandler {
        private RegistryConfig registryConfig;
    
        private final RegistryClient registryClient;
    
        public RegistryHandler(RegistryConfig registryConfig) {
            this.registryConfig = registryConfig;
            this.registryClient = new RegistryClient(this.registryConfig);
        }
    
        public boolean registry() {
            return registryClient.registry();
        }
    
        public String getImplementClass(String apiName) {
            return registryClient.getImplementClass(apiName);
        }
    }

    注册处理器RegistryHandler的作用是帮助服务提供端完成向注册中心的注册工作,初始化RegistryHandler时,会先初始化注册客户端RegistryClient,调用registry方法,将服务提供端的信息注册到注册中心(redis)中。

    /**
     * @Author zxm
     * @Description
     * @Date Create in 下午 4:51 2019/1/22 0022
     */
    public class RegistryClient {
        private RegistryConfig registryConfig;
    
        private JedisTemplate template;
    
        public RegistryClient(RegistryConfig registryConfig) {
            this.registryConfig = registryConfig;
            template = JedisTemplateFactory.newInstance(registryConfig.getRegistryHost(), registryConfig.getRegistryPort());
        }
    
        public boolean registry(){
            String key = template.joinKey(registryConfig.getServerName(), registryConfig.getLocalHostAddrss(), registryConfig.getPort());
            String result = template.setValue(key, buildRegistryValue(registryConfig.getProviderConfigs()));
    
            if (null != result) {
                return true;
            }
            return false;
        }
    
        private String buildRegistryValue(Set<ProviderConfig> providerConfigs) {
            JSONObject value = new JSONObject();
            providerConfigs.forEach(providerConfig -> value.put(providerConfig.getApiName(), providerConfig.getImplementsClass()));
            return value.toJSONString();
        }
    
        /**
         * 根据apiName获取实现类
         * @param apiName
         * @return
         * @throws UnknownHostException
         */
        public String getImplementClass(String apiName){
            String key = template.joinKey(registryConfig.getServerName(), registryConfig.getLocalHostAddrss(), registryConfig.getPort());
            String result = template.getValue(key);
            return JSONObject.parseObject(result).get(apiName).toString();
        }
    }
    

    注册的基本原理就是将提供者的接口信息保存在redis中,消费端启动的时候,访问redis数据,获取提供者的ip及接口信息列表。

    至此,注册工作基本完成!

    我们的框架的工作一个是服务发现,另一个就是实现rpc通信,提供端暴露完接口后,就要随时等待消费端的调用,下面我们看一下提供端是如何处理rpc调用逻辑的。

    /**
     * @Author zxm
     * @Description zxm
     * @Date Create in 下午 2:53 2019/1/28 0028
     */
    public class ProviderProxyInvoker {
        private RegistryConfig registryConfig;
    
        private RegistryHandler registryHandler;
    
        public ProviderProxyInvoker(RegistryConfig registryConfig, RegistryHandler registryHandler) {
            if (null == registryConfig) {
                throw new RuntimeException("registryConfig is not null");
            }
            this.registryConfig = registryConfig;
            this.registryHandler = registryHandler;
            init(this.registryConfig);
        }
    
        /**
         * 异步初始化nettyServer
         * @param registryConfig
         */
        private void init(RegistryConfig registryConfig) {
            try {
                new Thread(new NettySocketServer(registryConfig.getPort(), this)).start();
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            }
        }
    
        /**
         * 反射调用实现类
         * @param rpcProtocol
         * @return
         */
        public Object invoke(RpcProtocol rpcProtocol) {
            try {
                Class implementClass = Class.forName(registryHandler.getImplementClass(rpcProtocol.getInterfaceName()));
                Method method = Class.forName(rpcProtocol.getInterfaceName())
                        .getMethod(rpcProtocol.getMethodName(), rpcProtocol.getParameterTypes());
                return method.invoke(implementClass.newInstance(), rpcProtocol.getArgs());
            } catch (ClassNotFoundException ex1) {
                throw new RuntimeException("api " + rpcProtocol.getInterfaceName() + " is not found, msg is:" + ex1.getMessage());
            } catch (NoSuchMethodException ex2) {
                throw new RuntimeException("method " + rpcProtocol.getMethodName() + " is not found");
            } catch (IllegalAccessException e) {
                return null;
            } catch (InvocationTargetException e) {
                return null;
            } catch (InstantiationException e) {
                return null;
            }
        }
    }

    ProviderProxyInvoker作为提供端最核心的组件,主要完成网络通信层(nettyServer)的初始化以及反射调用接口实现类的工作。在初始化ProviderProxyInvoker时,会先初始化netty的server端,等待客户端的请求。

      /**
         * 异步初始化nettyServer
         * @param registryConfig
         */
        private void init(RegistryConfig registryConfig) {
            try {
                new Thread(new NettySocketServer(registryConfig.getPort(), this)).start();
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage());
            }
        }

    我们的rpc通信协议主要是基于tcp的,因此我们底层采用netty实现,看一下初始化逻辑:

    /**
     * @Author zxm
     * @Description netty服务端
     * @Date Create in 下午 2:19 2018/6/15 0015
     */
    @ChannelHandler.Sharable
    public class NettySocketServer implements Runnable{
        private static final Log log = LogFactory.getLog(NettySocketServer.class);
    
        private int port;
    
        private ProviderProxyInvoker invoker;
    
        public NettySocketServer(Integer port, ProviderProxyInvoker invoker) throws Exception {
            if (null == port) {
                this.port = 8888;
            } else {
                this.port = port.intValue();
            }
    
            if (invoker != null) {
                this.invoker = invoker;
            }
        }
    
        @Override
        public void run() {
            try {
                this.init(port);
            } catch (Exception e) {
            }
        }
    
        private void init(int port) throws Exception {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
    
            try {
                ServerBootstrap bootstrap = new ServerBootstrap();
                bootstrap.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        //保持连接数
                        .option(ChannelOption.SO_BACKLOG, 1024)
                        //有数据立即发送
                        .childOption(ChannelOption.TCP_NODELAY, true)
                        //保持长连接
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel socketChannel) {
                                socketChannel
                                        .pipeline()
                                        .addLast(new StringDecoder(), new StringEncoder(), new NettyServerReadHandler(invoker));
                            }
                        });
    
                ChannelFuture f = bootstrap.bind(port).sync();
    
                if (f.isSuccess()) {
                    log.info("netty server started success!!!\r\n(Copyright© Nicholas.Tony)");
                } else {
                    log.info("long connection started fail");
                }
    
    
                f.channel().closeFuture().sync();
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }
    }
    /**
     * @Author zxm
     * @Description 服务端数据读取处理器
     * @Date Create in 下午 2:37 2018/6/15 0015
     */
    public class NettyServerReadHandler extends SimpleChannelInboundHandler<String> {
        Logger log = LoggerFactory.getLogger(NettyServerReadHandler.class);
    
        private ProviderProxyInvoker invoker;
    
        public NettyServerReadHandler(ProviderProxyInvoker invoker) {
            if (invoker != null) {
                this.invoker = invoker;
            }
        }
    
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) {
            channelReadExecute(ctx.channel(), msg);
        }
    
        private void channelReadExecute(Channel channel, String msg) {
            log.info("remote [{}] request success, params is:{}", channel.remoteAddress(), msg);
            NettyServerDispatcher.threadPool.submit(new NettyServerDispatcherHandler(invoker, channel, msg));
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            ctx.flush();
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            ctx.close();
        }
    
        @Override
        public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            super.channelRegistered(ctx);
            log.info("client " + ctx.channel().remoteAddress() + " connected");
        }
    
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            super.channelInactive(ctx);
        }
    }

    消息监听模块,采用线程池异步进行处理,提高并发能力,实际处理请求的处理器是NettyServerDispatcherHandler组件,将客户端的请求报文解析后,在提供端本地利用java反射机制,完成接口的调用工作,然后将调用结果同步返回给netty客户端。

    /**
     * @Author zxm
     * @Description
     * @Date Create in 下午 3:34 2019/1/30 0030
     */
    public class NettyServerDispatcherHandler implements Runnable {
        private ProviderProxyInvoker invoker;
    
        private Channel channel;
        private String msg;
    
        public NettyServerDispatcherHandler(ProviderProxyInvoker invoker, Channel channel, String msg) {
            this.invoker = invoker;
            this.channel = channel;
            this.msg = msg;
        }
    
        @Override
        public void run() {
            RpcProtocol rpcProtocol = RpcSerializer.deSerialize(msg, RpcProtocol.class);
            Object response = invoker.invoke(rpcProtocol);
            channel.writeAndFlush(RpcSerializer.serialize(rpcResultWrapper(rpcProtocol.getId(), response)));
        }
    
        private RpcResult rpcResultWrapper(String id, Object response) {
            RpcResult rpcResult = new RpcResult();
            rpcResult.setId(id);
            rpcResult.setValue(response);
            return rpcResult;
        }
    }

    至此提供端rpc通信模块的相关组件逻辑的实现基本完成,总结下来,原理也比较简单。在下一篇中我们会继续分享消费端的相关实现。

    项目地址: https://github.com/zhangxiaomin1993/rpc-server-sdk

    声明:文章和项目不以商业盈利为目的,仅作为本人技术积累的沉淀,分享给大家,有兴趣的朋友欢迎访问交流,共同学习和进步!大佬和专家路过,不喜勿喷!

    展开全文
  • 目录 Netflix Eureka 简介 spring-cloud-netflix简介 Eureka 原理 服务发现 ...1、Eureka 是 Netflix 公司开发的服务发现框架,Spring Cloud 对它提供了支持,将它集成在了自己的spring-cl...

    目录

    Netflix Eureka 简介

    spring-cloud-netflix 简介

    Eureka 原理

    服务发现

    客户端发现模式

    服务端发现模式

    服务注册表

    自注册方式

    第三方注册模式

    总结


    Netflix Eureka 简介

    1、Eureka 是 Netflix 公司开发的服务发现框架,Spring Cloud 对它提供了支持,将它集成在了自己的 spring-cloud-netflix  子项目中。

    2、Netflix 公司在 Github 上开源了很多项目,Eureka 只是其中一个,Netflix 开源主页:https://github.com/Netflix

    3、Netflix Eureka GitHub 开源地址:https://github.com/Netflix/eureka

    AWS Service registry for resilient mid-tier load balancing and failover.(Eureka 是用于弹性中间层负载平衡和故障转移的AWS服务注册中心)

    4、Eureka 是一种基于 REST(表现层状态转换) 的服务,主要用于 AWS(Amazon Web Services-亚马逊web服务 云中定位服务,以实现中间层服务器的负载平衡和故障转移。

    5、The build requires java8 because of some required libraries that are java8 (servo), but the source and target compatibility are still set to 1.7.(构建 Eureka 项目需要 Java JDK 1.8以上版本,因为其中一些必须的库使用了 Java8)

    6、Netflix  Eureka 官方文档:https://github.com/Netflix/eureka/wiki,目前最新版是 2019年1月11更新的 V1.9.9

    7、Netflix  Eureka 官网原来是 2.X 版本的,后面因为某些原因停止了 2.X 版本的维护,但是 1.X 版本仍然活跃,仍在积极开发、维护、和使用,WIKI 中公示如下:

    Eureka 2.0 (Discontinued)(Eureka2.0 已经停止)

    The existing open source work on eureka 2.0 is discontinued. The code base and artifacts that were released as part of the existing repository of work on the 2.x branch is considered use at your own risk.Eureka 1.x is a core part of Netflix's service discovery system and is still an active project.

    (现有的 Eureka 2.0开源工作已经停止。继续使用 2.x 分支上的代码将由您自己承担风险。Eureka 1.x 是 Netflix 服务发现系统的核心部分,仍然是一个活跃的项目。)

    spring-cloud-netflix 简介

    1、Spring Cloud 将 Netflix Eureka 集成在其子项目 spring-cloud-netflix 中,以实现 Spring Cloud 的服务发现功能。

    2、spring-cloud-netflix 子项目包含的不仅仅只有 Eureka,使用 Netflix 组件构建大型分布式系统,提供的模式包括服务发现(Eureka)断路器(Hystrix)智能路由(Zuul)、以及客户端负载平衡(Ribbon)

    3、Spring Cloud Netflix features(特性):

    编号特性描述
    1Service Discovery(服务发现)可以注册 Eureka 实例,客户端可以使用 spring 管理的 bean 发现实例 
    2Service Discovery(服务发现)可以使用声明性 Java 配置创建嵌入式 Eureka 服务器
    3Circuit Breaker(断路器)Hystrix 客户端可以用一个简单的注解驱动的方法装饰器来构建
    4Circuit Breaker(断路器)带有声明性 Java 配置的嵌入式 Hystrix 仪表板
    5Declarative REST Client(声明性REST客户端)

    Feign创建了一个用JAX-RS或Spring MVC注释装饰的接口的动态实现

    客户端负载均衡器:Ribbon

    6Client Side Load Balancer(客户端负载均衡器)Ribbon
    7External Configuration(外部配置)从Spring环境到Archaius的桥梁(使用Spring引导约定支持Netflix组件的本地配置)
    8Router and Filter(路由器和过滤器)zuul过滤器的自动重新定位,以及反向创建代理的配置方法的简单约定

    Eureka 原理

    1、官网介绍地址:https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance

    2、Eureka 官方的“区域”概念来自 AWS(Amazon Web Services-亚马逊web服务),亚马逊为全球提供云服务,所以在全球范围内划分了不同的区域,如下是其中的8个区域,另外还有中国区,以及专门为美国政府使用的一个区。

    3、一个区域可以包含多个可用区。

    3、Eureka 高层体系结构图就是针对单个区域中的多个可用区进行说明的,us-eat-1c、us-eat-1d、us-eat-1e 表示为同一个区域( us-eat-1)中的 3 个可用区。

    4、上图来自 Eureka 官方架构图,描述了 Eureka 集群的工作过程。每个区域都有一个 Eureka 集群,它只知道其区域中的实例,每个区域至少有一个 Eureka 服务器来处理区域故障。

    1)Application Service:服务提供者

    2)Application Client:服务消费者

    3)Make Remote Call:可以简单理解为调用 RESTful 的接口

    4)us-east-1c、us-east-1d ...都是可以区,都属于 us-east-1 这个region(区域)

    5、如图所示 Eureka 包含两个组件:Eureka Server 和 Eureka Client。 Eureka Server 提供服务注册服务,各个节点启动后,会在 Eureka Server 中进行注册,这样 Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。

    6、Eureka Client 是一个 Java 客户端,用于简化与 Eureka Server 的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。

    7、应用启动后,Eureka Client 将会向 Eureka Server发送心跳(默认周期为30秒),如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。

    8、Eureka Server 之间将会通过复制(Replicate)的方式完成数据的同步。

    9、Eureka 还提供了客户端缓存的机制,即使所有的 Eureka Server 都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。 

    10、Eureka 通过心跳检测、健康检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。

    服务发现

    为什么要使用服务发现?

    1、运行在物理硬件上的传统应用中,服务实例的网络位置是相对固定的,代码能从一个偶尔更新的配置文件中读取网络位置。对于基于云端的、现代化的微服务应用而言,这却是一大难题。

    服务实例的网络位置都是动态分配的,由于服务扩展升级、失败宕机等等,服务实例会经常动态改变,因此客户端代码需要使用更加复杂的服务发现机制。

    服务发现有两大模式:客户端发现模式服务端发现模式

    客户端发现模式

    1、使用客户端发现模式时,客户端决定相应服务实例的网络位置,并且对请求实现负载均衡。客户端查询服务注册表,然后使用负载均衡算法从中选择一个实例,并发出请求。

    2、服务注册表是一个可用服务实例的数据库,其中包含所有可用的服务实例,客户端从服务注册服务中查询,然后使用负载均衡算法从多个服务实例中选择出一个服务实例,最后发出请求。

    3、服务实例的网络位置在启动时被记录到服务注册表,等实例终止时被删除,服务实例的注册信息通常使用心跳机制来定期刷新(如 Eureka客户端默认会30秒发送一次心跳,超过90秒未收到心跳,则会被移除)。

    4、Netflix Eureka 其中就有服务注册表功能,为服务实例注册管理和查询可用实例提供了 REST API 接口,Netflix Ribbon 是 IPC 客户端,与 Eureka 一起实现对请求的负载均衡。(Eureka与Ribbon都已经集成在了Rispring-cloud-netflix项目中的)

    5、客户端发现模式优缺点兼有。这一模式相对直接,除了服务注册外,其它部分无需变动。此外,由于客户端知晓可用的服务实例,能针对特定应用实现智能负载均衡,比如使用哈希一致性。这种模式的一大缺点就是客户端与服务注册绑定,要针对服务端用到的每个编程语言和框架,实现客户端的服务发现逻辑。

    服务端发现模式

    Consul + Nginx,请参考:http://blog.daocloud.io/microservices-4/

    服务注册表

    1、服务注册表是服务发现的核心部分,是包含服务实例的网络地址的数据库。服务注册表需要高可用而且随时更新。客户端能够缓存从服务注册表中获取的网络地址,然而,这些信息最终会过时,客户端也就无法发现服务实例。因此,服务注册表会包含若干服务端,使用复制协议保持一致性。

    2、Netflix Eureka 是服务注册表的上好案例,为注册和请求服务实例提供了 REST API。服务实例使用 POST 请求来注册网络地址,每三十秒使用 PUT 请求来刷新注册信息。注册信息也能通过 HTTP DELETE 请求或者实例超时来被移除。以此类推,客户端能够使用 HTTP GET 请求来检索已注册的服务实例。

    3、Netflix 通过在每个 AWS EC2 域运行一个或者多个 Eureka 服务实现高可用性。每个 Eureka 服务器都运行在拥有弹性 IP 地址的 EC2 实例上。DNS TEXT 记录被用来保存 Eureka 集群配置,后者包括可用域和 Eureka 服务器的网络地址列表。Eureka 服务在启动时会查询 DNS 去获取 Eureka 集群配置,确定同伴位置,以及给自己分配一个未被使用的弹性 IP 地址。

    4、Eureka 客户端,包括服务和服务客户端,查询 DNS 去发现 Eureka 服务的网络地址。客户端首选同一域内的 Eureka 服务。然而,如果没有可用服务,客户端会使用其它可用域中的 Eureka 服务。

    5、除了 Eureka ,主流的服务发现组件(提供服务注册表)还有:

    1)etcd – 高可用、分布式、一致性的键值存储,用于共享配置和服务发现。Kubernetes 和 Cloud Foundry 是两个使用 etcd 的著名项目。

    2)consul – 发现和配置的服务,提供 API 实现客户端注册和发现服务。Consul 通过健康检查来判断服务的可用性。

    3)Apache ZooKeeper – 被分布式应用广泛使用的高性能协调服务。Apache ZooKeeper 最初是 Hadoop 的子项目,现在已成为顶级项目。

    6、服务实例必须在注册表中注册和注销。注册和注销有两种不同的方法。方法一是服务实例自己注册,也叫自注册模式(self-registration pattern);另一种是采用管理服务实例注册的其它系统组件,即第三方注册模式

    自注册方式

    1、当使用自注册模式时,服务实例负责在服务注册表中注册和注销,另外,如果需要的话,一个服务实例也要发送心跳来保证注册信息不会过时。

    2、Netflix OSS Eureka 客户端是非常好的案例,它负责处理服务实例的注册和注销。Spring Cloud 能够执行包括服务发现在内的各种模式,使得利用 Eureka 自动注册服务实例更简单,只需要给 Java 配置类注释 @EnableEurekaClient。

    3、自注册模式优缺点兼备。它相对简单,无需其它系统组件。然而,它的主要缺点是把服务实例和服务注册表耦合,必须在每个编程语言和框架内实现注册代码。

    第三方注册模式

    1、使用第三方注册模式,服务实例则不需要向服务注册表注册,而由被称为服务注册器的另一个系统模块会处理。

    2、服务注册器会通过查询部署环境或订阅事件的方式来跟踪运行实例的更改,一旦侦测到有新的可用服务实例,会向注册表注册此服务,服务管理器也负责注销终止的服务实例

    3、Registrator 是一个开源的服务注册项目,它能够自动注册和注销被部署为 Docker 容器的服务实例。Registrator 支持包括 etcd 和 Consul 在内的多种服务注册表。

    4、Netflix OSS Prana 是另一个服务注册器,主要面向非 JVM 语言开发的服务,是一款与服务实例一起运行的并行应用。Prana 使用 Netflix Eureka 来注册和注销服务实例。

    5、服务注册器是部署环境的内置组件,由 Autoscaling Group 创建的 EC2 实例能够自动向 ELB 注册,Kubernetes 服务自动注册并能够被发现。

    6、第三方注册模式也是优缺点兼具。在第三方注册模式中,服务与服务注册表解耦合,无需为每个编程语言和框架实现服务注册逻辑;相反,服务实例通过一个专有服务以中心化的方式进行管理。它的不足之处在于,除非该服务内置于部署环境,否则需要配置和管理一个高可用的系统组件。

    总结

    1、在微服务应用中,服务实例的运行环境会动态变化,实例网络地址也是如此,因此客户端为了访问服务必须使用服务发现机制。

    2、服务注册表是服务发现的关键部分,服务注册表是可用服务实例的数据库,提供管理 API 和查询 API,服务实例使用管理 API 来实现注册和注销,系统组件使用查询 API 来发现可用的服务实例。

    3、服务发现有两种主要模式:客户端发现和服务端发现。在使用客户端服务发现的系统中,客户端查询服务注册表,选择可用的服务实例,然后发出请求。在使用服务端发现的系统中,客户端通过路由转发请求,路由器查询服务注册表并转发请求到可用的实例。

    4、服务实例的注册和注销也有两种方式。一种是服务实例自己注册到服务注册表中,即自注册模式;另一种则是由其它系统组件处理注册和注销,也就是第三方注册模式。

    5、在一些部署环境中,需要使用 Netflix Eureka、etcd、Apache Zookeeper 等服务发现来设置自己的服务发现基础设施。而另一些部署环境则内置了服务发现。例如,Kubernetes 和 Marathon 处理服务实例的注册和注销,它们也在每个集群主机上运行代理,这个代理具有服务端发现路由的功能。

    6、HTTP 反向代理和 Nginx 这样的负载均衡器能够用做服务器端的服务发现均衡器。服务注册表能够将路由信息推送到 Nginx 激活配置更新,譬如使用 Cosul Template。Nginx Plus 支持额外的动态配置机制,能够通过 DNS 从注册表中获取服务实例的信息,并为远程配置提供 API。

    英文原文:https://www.nginx.com/blog/service-discovery-in-a-microservices-architecture/

    翻译原文:http://blog.daocloud.io/microservices-4/

     

     

     

    展开全文
  • RPC框架服务的注册与发现

    千次阅读 2017-12-19 00:00:07
    RPC框架中有3个重要的角色: 注册中心 :保存所有服务的名字,服务提供者的ip列表,服务消费者的IP列表 服务提供者: 提供跨进程服务 服务消费者: 寻找到指定命名的服务并消费 注册中心维持着一个服务配置中心节点...
  • 包括常见的RPC框架、常见的序列化/反序列化方案及选型、分布式服务框架服务的发布引入实现细节、软负载实现、底层通信方案实现、服务注册与发现实现、服务治理常见的功能等。通过对这些知识点的逐步讲解,层层深入,...
  • java框架篇spring cloud之服务发现consul

    千次阅读 2018-09-11 10:55:58
    1 前言 上篇文章提到利用eureka做服务发现,spring config server做集中配置,但是由于eureka 2.0已经停止开源开发,建议开发者切换到consul...spring cloud提供了多个服务发现框架集成,euerka已经停止开发了,目...
  • 游戏服务器开源框架(xinyue-game-frame)

    千次阅读 2020-03-24 08:56:02
    今天给大家介绍一个开源的游戏框架,它是基于Spring Cloud + Netty实现的一个分布式游戏服务器框架,支持负载均衡,集群部署,动态扩展和伸缩,能基本满足休闲游,卡牌游戏,SLG游戏的服务器框架快速搭建。此框架...
  • 在后续一段时间里, 我会写一系列文章来讲述如何实现一个RPC框架。 这是系列第三篇文章, 主要讲述了服务注册和服务发现这一块。 在系列的第一篇文章中...对于服务发现,现在有很多可供选择的工具,例如zookeeper, et
  • 阿里HSF(服务框架

    万次阅读 2019-06-02 20:00:28
    HSF(服务框架) 简介 高速服务框架 HSF (High-speed Service Framework),是在阿里巴巴内部广泛使用的分布式 RPC 服务框架。 HSF 联通不同的业务系统,解耦系统间的实现依赖。HSF 从分布式应用的层面,统一了服务的...
  • 分布式服务框架 HSF

    千次阅读 2015-08-14 17:09:48
    摘要:RPC 协议采用多路复用的 TCP 长连接方式,在服务提供者和调用者间有多个服务请求同时调用时会共用同一个长连接,即一个连接交替传输不同请求的字节块。它既避免了反复建立连接开销,也避免了连接的等待闲置...
  • 分布式、集群、分布式服务框架

    千次阅读 2018-02-22 16:19:45
    分布式服务框架一般可以分为以下几个部分, (1)RPC基础层: 包括底层通信框架,如NIO框架、通信协议,序列化和反序列化协议, 以及在这几部分上的封装,屏蔽底层通信细节和序列化方式差异 (2)服务发布/消费: ...
  • 高性能分布式游戏服务器框架

    万次阅读 2017-05-21 22:39:46
    欢迎大家Fork mqant开源框架 为什么决定要重新造一个轮子? 目前网上优秀的开源游戏服务器框架也不少(当然与web框架比起来就少太多了),但总结起来都各有各的优缺点,下面列出我在选型过程中的一些考量,希望大家能...
  • SpringCloud服务治理框架

    万次阅读 2018-10-30 09:21:47
    服务治理是微服务架构中核心模块,它主要用来实现各个微服务实例的自动化注册、发现、续约和销毁。 Spring Cloud Eureka是Spring Cloud Netflix微服务套件中的一部分,它基于Netflix Eureka做了二次封装。主要负责...
  • 开源游戏服务器框架汇总

    千次阅读 2019-11-11 17:01:15
    有哪些开源游戏服务器框架,值得学习呢。基于node.js 、java、C#、golang 、c++、python 等技术栈有各种各样的游戏框架。 本文收集一些比较常用的 github上star和fork有一定数量的较为完整的框架 skynet 云风大神的...
  • 蒙OS开源代码精要解读之—— 系统服务框架子系统(服务启动) 作者介绍: 中科创达OpenHarmony研究组 说明: 中科创达OpenHarmony研究组第一时间对https://codechina.csdn.net/openharmony上开源的代码进行了详尽...
  • 小米1MIUI开发版可用的google服务框架

    千次阅读 2021-02-07 03:21:15
    小米1刷了最新的MIUI(4.2.7),里面不在带有google的服务框架了。这对于我这等需要它的人来说是个问题。从小米的应用商店下载安装了谷歌应用下载器,用他下载了google服务框架,但是不能用,闪退。没办法,各方搜索,...
  • 框架核心:根据作者的描述,Skynet的核心功能就是解决一个问题: 把一个符合规范的C模块,从动态库(so文件)中启动起来,绑定一个永不重复(即使模块退出)的数字id做为其 handle 。模块被称为服务(Service),...
  • Eureka服务发现注册详解

    万次阅读 多人点赞 2019-06-25 16:48:38
    Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以...
  • 作者:章耿,原京东资深架构师,曾负责京东服务框架,配置中心等基础平台。近十年工作经验,专注于基础中间件等底层技术架构,对分布式系统/服务化/DevOps建设有一定经验。  |前言  首先本文不讨论为什么要...
  • 分布式服务框架服务治理

    千次阅读 2017-04-02 21:10:00
    分布式服务框架服务治理的一些点~~~
  • 编者按:J2EE异军突起之时,那些2层架构(web+后端)的人必定是崩溃的;而后spring以without ejb...多点生活架构师陈泽洪本着【少谈些概念、多解决问题】的原则给我们分享他们用几个月完成的这一套分布式服务框架...
  • SpringCloud微服务框架02-Eureka服务注册与发现 SpringCloud微服务框架03 - Ribbon负载均衡 SpringCloud微服务框架04 - Config统一配置中心 文章中设计到的项目源码,会逐步整理到github上。github除了本系列文章...
  • 构建服务共享体系,必然需要采用一套服务框架来支持整个服务体系的运转。下面介绍淘宝从单一系统模式转化为服务框架的过程。阐述为什么 “去中心化”服务框架成为今天绝大数互联网公司的选择。 淘宝平台...
  • 工作中到底有哪些开源游戏服务器框架,该去值得学习呢? 囊括到node.js 、java、C#、golang 、c++、python 等技术栈有各种各样的游戏框架。 本文给大家总结了一些github上star和fork比较常用的且有一定数量的较为...
  • google服务框架

    千次阅读 2014-05-08 11:44:12
    看到有人要google服务包,由于游戏验证的需要和我自己对Google Play的钟情,【装服务框架和PLAY就能玩所有游戏.换飞行模式,用市场解锁能下载市场免费.钛备份+信用卡,付费下载15分钟内退款,钛备份恢复,你就等于破解了...
  • 我需要唯一标识一个Android设备...因此,寻找在每个设备上工作的东西,我偶然发现了GSF ID KEY(谷歌服务框架ID)。你们认为这是一个可靠且始终有效的解决方案吗? 这是我发现检索GSF ID KEY代码:GSF ID KEY(谷歌服务...
  • 荐书:《架构探险:从零开始写分布式服务框架》 一线技术专家 全方位解析 分布式服务框架底层技术细节 手把手教你 搭建一个完整的符合自身需求的 分布式服务框架 随着互联网浪潮风起云涌,互联网行业...
  • 苏宁的RPC远程服务调用框架RSF

    千次阅读 多人点赞 2018-09-28 10:17:30
    苏宁的RPC远程服务调用框架RSF    苏宁的系统间交互最初使用中心化 ESB 架构,但随着系统拆分工作的展开及业务量的迅速攀升,系统间调用规模越来越大,ESB 中心化架构带来的诸如中心资源隔离、中心容量动态评估...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 485,870
精华内容 194,348
关键字:

服务发现的框架