栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Eureka服务治理代码实例和原理机制详解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Eureka服务治理代码实例和原理机制详解

文章目录
  • 代码示例
    • 启动一个服务注册中心
    • 注册服务提供者
    • 高可用注册中心
    • 服务的发现与消费
  • Eureka服务治理基础架构原理

代码示例 启动一个服务注册中心

通过@EnableEurekaServer注解启动一个服务注册中心提供给其他应用进行对话。这一步非常简单,只需在一个普通的Spring Boot应用中添加这个注解就能开启此功能,比如下面的例子:

 @EnableEurekaServer 
@SpringBootApplication 
public class Application { 
  public static void main(String[] args) { 
    new SpringApplicationBuilder(Application.class).web(true).run(args); 
  } 

在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,只需在application.properties中增加如下配置:

server.port=1111 

eureka.instance.hostname=localhost 
eureka.client.register-with-eureka=false 
eureka.client.fetch-registry=false 
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

由于后续内容也都会在本地运行,为了与后续要进行注册的服务区分,这里将服务注册中心的端口通过server.port属性设置为1111。 eureka.client.register-with-eureka:由于该应用为注册中心,所以设置为false,代表不向注册中心注册自己。 eureka.client.fetch-registry:由于注册中心的职责就是维护服务实例,它并不需要去检索服务,所以也设置为false。 在完成了上面的配置后,启动应用并访问http://localhost:1111/。可以看到如下图所示的Eureka信息面板,其中Instances currently registered with Eureka栏是空的,说明该注册中心还没有注册任何服务。

注册服务提供者

在完成了服务注册中心的搭建之后,接下来我们尝试将一个既有的Spring Boot应用加入Eureka的服务治理体系中去。 可以使用上一章中实现的快速入门工程来进行改造,将其作为一个微服务应用向服务注册中心发布自己。首先,修改pom.xml,增加Spring Cloud Eureka模块的依赖,具体代码如下所示:

        

              org.springframework.boot             spring-boot-starter-web                   
                 org.springframework.boot             spring-boot-starter-test             test         
              
                 org.springframework.cloud             spring-cloud-starter-eureka              
      
         
                 
                         org.springframework.cloud                 spring-cloud-dependencies                 Brixton.SR5                 pom                 
            import             
                 
         
 


改造/hello请求处理接口,通过注入DiscoveryClient对象,在日志中打印出服务的相关内容。

@RestController 
public class HelloController { 

  private final Logger logger = Logger.getLogger(getClass()); 

  @Autowired 
  private DiscoveryClient client; 

  @RequestMapping(value = "/hello", method = RequestMethod.GET) 
  public String index() { 
    ServiceInstance instance = client.getLocalServiceInstance();
  logger.info("/hello, host:" + instance.getHost() + ", service_id:" + instance.getServiceId()); 
        return "Hello World"; 
    } 
 
}
//然后,在主类中通过加上@EnableDiscoveryClient注解,激活Eureka中的DiscoveryClient实现(自动化配置,创建DiscoveryClient接口针对Eureka客户端的EurekaDiscoveryClient实例),才能实现上述Controller中对服务信息的输出。
@EnableDiscoveryClient 
@SpringBootApplication 
public class HelloApplication { 
 
    public static void main(String[] args) { 
        SpringApplication.run(HelloApplication.class, args); 
    } 
 
} 

最后application.properties配置文件中,通过spring.application.name属性来为服务命名,比如命名为hello-service。再通过eureka.client.serviceUrl.defaultZone属性来指定服务注册中心的地址,这里我们指定为之前构建的服务注册中心地址,完整配置如下所示:

spring.application.name=hello-service 

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/ 

下面我们分别启动服务注册中心以及这里改造后的hello-service服务。在hello-service服务控制台中,Tomcat启动之后,com.netflix.discovery.DiscoveryClient对象打印了该服务的注册信息,表示服务注册成功。

我们也可以通过访问Eureka的信息面板,在Instances currently registered with Eureka一栏中看到服务的注册信息。

高可用注册中心

在微服务架构这样的分布式环境中,我们需要充分考虑发生故障的情况,所以在生产环境中必须对各个组件进行高可用部署,对于微服务如此,对于服务注册中心也一样。但是到本节为止,我们一直都在使用单节点的服务注册中心,这在生产环境中显然并不合适,我们需要构建高可用的服务注册中心以增强系统的可用性。 Eureka Server的设计一开始就考虑了高可用问题,在Eureka的服务治理设计中,所有节点即是服务提供方,也是服务消费方,服务注册中心也不例外。是否还记得在单节点的配置中,我们设置过下面这两个参数,让服务注册中心不注册自己: eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

**Eureka Server的高可用实际上就是将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组互相注册的服务注册中心,**以实现服务清单的互相同步,达到高可用的效果。

下面我们就来尝试搭建高可用服务注册中心的集群。可以在上面实现的服务注册中心的基础之上进行扩展,构建一个双节点的服务注册中心集群。 创建application-peer1.properties,作为peer1服务中心的配置,并将serviceUrl指向peer2:

 spring.application.name=eureka-server 
server.port=1111 

eureka.instance.hostname=peer1 
eureka.client.serviceUrl.defaultZone=http://peer2:1112/eureka/ 

创建application-peer2.properties,作为peer2服务中心的配置,并将serviceUrl指向peer1:

spring.application.name=eureka-server 
server.port=1112 

eureka.instance.hostname=peer2 
eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/ 

在/etc/hosts文件中添加对peer1和peer2的转换,让上面配置的host形式的serviceUrl能在本地正确访问到;

Windows系统路径为C:WindowsSystem32driversetchosts。 127.0.0.1 peer1
127.0.0.1 peer2 通过spring.profiles.active属性来分别启动peer1和peer2:java -jar eureka-server-1.0.0.jar --spring.profiles.active=peer1
java -jar eureka-server-1.0.0.jar --spring.profiles.active=peer2

此时访问peer1的注册中心http://localhost:1111/,如下图所示,我们可以看到,registered-replicas中已经有peer2节点的eureka-server了。同样的,我们访问peer2的注册中心http://localhost:1112/,也能看到registered-replicas中已经有peer1节点,并且这些节点在可用分片(available-replicase)之中。我们也可以尝试关闭peer1,刷新http://localhost:1112/,可以看到peer1的节点变为了不可用分片(unavailable-replicas)。

在设置了多节点的服务注册中心之后,服务提供方还需要做一些简单的配置才能将服务注册到Eureka Server集群中。我们以hello-service为例,修改application.properties配置文件,如下所示:

spring.application.name=hello-service 

eureka.client.serviceUrl.defaultZone=http://peer1:1111/eureka/,http://peer2:1112/eureka/ 

上面的配置主要对eureka.client.serviceUrl.defaultZone属性做了改动,将注册中心指向了之前我们搭建的peer1与peer2。

服务的发现与消费

通过上面的内容介绍与实践,我们已经搭建起微服务架构中的核心组件—服务注册中心(包括单节点模式和高可用模式)。同时,还对上一章中实现的Spring Boot入门程序做了改造。通过简单的配置,使该程序注册到Eureka注册中心上,成为该服务治理体系下的一个服务,命名为hello-service。现在我们已经有了服务注册中心和服务提供者,下面就来尝试构建一个服务消费者,它主要完成两个目标,发现服务以及消费服务。其中,服务发现的任务由Eureka的客户端完成,而服务消费的任务由Ribbon完成。Ribbon是一个基于HTTP和TCP的客户端负载均衡器,它可以在通过客户端中配置的ribbonServerList服务端列表去轮询访问以达到均衡负载的作用。当Ribbon与Eureka联合使用时,Ribbon的服务实例清单RibbonServerList会被DiscoveryEnabledNIWSServerList重写,扩展成从Eureka注册中心中获取服务端列表。同时它也会用NIWSDiscoveryPing来取代IPing,它将职责委托给Eureka来确定服务端是否已经启动。在本章中,我们对Ribbon不做详细的介绍,读者只需要理解它在Eureka服务发现的基础上,实现了一套对服务实例的选择策略,从而实现对服务的消费。下一章我们会对Ribbon做详细的介绍和分析。 下面我们通过构建一个简单的示例,看看在Eureka的服务治理体系下如何实现服务的发现与消费。 首先,我们做一些准备工作。启动之前实现的服务注册中心eureka-server以及hello-service服务,为了实验Ribbon的客户端负载均衡功能,我们通过java-jar命令行的方式来启动两个不同端口的hello-service,具体如下:

java -jar hello-service-0.0.1-SNAPSHOT.jar --server.port=8081
java -jar hello-service-0.0.1-SNAPSHOT.jar --server.port=8082

在成功启动两个hello-service服务之后,如下图所示,从Eureka信息面板中可以看到名为HELLO-SERVICE的服务中出现了两个实例单元,分别是通过命令行启动的8081端口和8082端口的服务。

创建一个Spring Boot的基础工程来实现服务消费者,取名为ribbon-consumer,并在pom.xml中引入如下的依赖内容。较之前的hello-service,我们新增了Ribbon模块的依赖spring-cloud-starter-ribbon。

     

org.springframework.boot     spring-boot-starter-parent     
    1.3.7.RELEASE
         
            
     
           org.springframework.boot       spring-boot-starter-web           
           org.springframework.cloud       spring-cloud-starter-eureka           
           org.springframework.cloud       spring-cloud-starter-ribbon         
    
     
           
                 org.springframework.cloud         spring-cloud-dependencies         Brixton.SR5         
            pom         
            import       
             
       

创建应用主类ConsumerApplication,通过@EnableDiscoveryClient注解让该应用注册为Eureka客户端应用,以获得服务发现的能力。

同时,在该主类中创建RestTemplate的Spring Bean实例,并通过@LoadBalanced注解开启客户端负载均衡。

@EnableDiscoveryClient 
@SpringBootApplication 
public class ConsumerApplication { 
    @Bean   
    @LoadBalanced   
    RestTemplate restTemplate() {     
        return new RestTemplate();   
    }   
    public static void main(String[] args) 
    {     SpringApplication.run(ConsumerApplication.class, args);   
    }  
} 

创建ConsumerController类并实现/ribbon-consumer接口。在该接口中,通过在上面创建的RestTemplate来实现对HELLO-SERVICE服务提供的/hello接口进行调用。**可以看到这里访问的地址是服务名HELLO-SERVICE,而不是一个具体的地址,**在服务治理框架中,这是一个非常重要的特性,也符合在本章一开始对服务治理的解释。

@RestController 

public class ConsumerController { 

@Autowired     
RestTemplate restTemplate;      
@RequestMapping(value = "/ribbon-consumer", method = RequestMethod.GET)     

public String helloConsumer() {         

return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();     

} 

 } 

在application.properties中配置Eureka服务注册中心的位置,需要与之前的HELLO-SERVICE一样,不然是发现不了该服务的,同时设置该消费者的端口为9000,不能与之前启动的应用端口冲突。

spring.application.name=ribbon-consumer 

server.port=9000  

eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/

启动ribbon-consumer应用后,我们可以在Eureka信息面板中看到,当前除了HELLO-SERVICE之外,还多了我们实现的RIBBON-CONSUMER服务。

通过向http://localhost:9000/ribbon-consumer发起GET请求,成功返回了“Hello World”。

此时,我们可以在ribbon-consumer应用的控制台中看到如下信息,Ribbon输出了当前客户端维护的HELLO-SERVICE的服务列表情况。其中包含了各个实例的位置,Ribbon就是按照此信息进行轮询访问,以实现基于客户端的负载均衡。另外还输出了一些其他非常有用的信息,如对各个实例的请求总数量、第一次连接信息、上一次连接信息、总的请求失败数量等。

再尝试发送几次请求,并观察启动的两个HELLO-SERVICE的控制台,可以看到两个控制台会交替打印下面的日志,

com.didispace.web.HelloController : /hello, host:PC-201602152056,
service_id:hello-service

这是我们之前在HelloController中实现的对服务信息的输出,可以用来判断当前ribbon-consumer对HELLO-SERVICE的调用是否是负载均衡的。

Eureka服务治理基础架构原理

Eureka服务治理基础架构的三个核心要素:
服务注册中心:Eureka提供的服务端,提供服务注册与发现的功能,也就是在上一节中我们实现的eureka-server。
服务提供者:提供服务的应用,可以是Spring Boot应用,也可以是其他技术平台且遵循Eureka通信机制的应用。它将自己提供的服务注册到Eureka,以供其他应用发现,也就是在上一节中我们实现的HELLO-SERVICE应用。
服务消费者:消费者应用从服务注册中心获取服务列表,从而使消费者可以知道去何处调用其所需要的服务,在上一节中使用了Ribbon来实现服务消费,另外后续还会介绍使用Feign的消费方式。 很多时候,客户端既是服务提供者也是服务消费者。

服务治理机制
在体验了Spring Cloud Eureka通过简单的注解配置就能实现强大的服务治理功能之后,我们来进一步了解一下Eureka基础架构中各个元素的一些通信行为,以此来理解基于Eureka实现的服务治理体系是如何运作起来的。以下图为例,其中有这样几个重要元素: “服务注册中心-1”和“服务注册中心-2”,它们互相注册组成了高可用集群。 “服务提供者”启动了两个实例,一个注册到“服务注册中心-1” 上,另外一个注册到“服务注册中心-2”上。 还有两个“服务消费者”,它们也都分别只指向了一个注册中心

根据上面的结构,下面我们来详细了解一下,从服务注册开始到服务调用,及各个元素所涉及的一些重要通信行为。
服务提供者

—服务注册
“服务提供者”在启动的时候会通过发送REST请求的方式将自己注册到Eureka Server上,同时带上了自身服务的一些元数据信息。Eureka Server接收到这个REST请求之后,将元数据信息存储在一个双层结构Map中,其中第一层的key是服务名,第二层的key是具体服务的实例名。(我们可以回想一下之前在实现Ribbon负载均衡的例子中,Eureka信息面板中一个服务有多个实例的情况,这些内容就是以这样的双层Map形式存储的。) 在服务注册时,需要确认一下eureka.client.register-with-eureka=true参数是否正确,该值默认为true。若设置为false将不会启动注册操作。

—服务同步

如架构图中所示,这里的两个服务提供者分别注册到了两个不同的服务注册中心上,也就是说,它们的信息分别被两个服务注册中心所维护。此时,由于服务注册中心之间因互相注册为服务,当服务提供者发送注册请求到一个服务注册中心时,它会将该请求转发给集群中相连的其他注册中心,从而实现注册中心之间的服务同步。通过服务同步,两个服务提供者的服务信息就可以通过这两台服务注册中心中的任意一台获取到。

—服务续约

在注册完服务之后,服务提供者会维护一个心跳用来持续告诉Eureka Server:“我还活着”,以防止Eureka Server的“剔除任务”将该服务实例从服务列表中排除出去,我们称该操作为服务续约(Renew)。 关于服务续约有两个重要属性,我们可以关注并根据需要来进行调整:
eureka.instance.lease-renewal-interval-in-seconds=30
eureka.instance.lease-expiration-duration-in-seconds=90 eureka.instance.lease-renewal-interval-in-seconds参数用于定义服务续约任务的调用间隔时间,默认为30秒。eureka.instance.lease-expiration-duration-in-seconds参数用于定义服务失效的时间,默认为90秒。 服务消费者取服务 到这里,在服务注册中心已经注册了一个服务,并且该服务有两个实例。当我们启动服务消费者的时候,它会发送一个REST请求给服务注册中心,来获取上面注册的服务清单。为了性能考虑,Eureka Server会维护一份只读的服务清单来返回给客户端,同时该缓存清单会每隔30秒更新一次。 获取服务是服务消费者的基础,所以必须确保eureka.client.fetch-registry=true参数没有被修改成false,该值默认为true。若希望修改缓存清单的更新时间,可以通过eureka.client.registry-fetch-interval-seconds=30参数进行修改,该参数默认值为30,单位为秒。

—服务调用

服务消费者在获取服务清单后,通过服务名可以获得具体提供服务的实例名和该实例的元数据信息。因为有这些服务实例的详细信息,所以客户端可以根据自己的需要决定具体调用哪个实例,在Ribbon中会默认采用轮询的方式进行调用,从而实现客户端的负载均衡。

—服务下线

在系统运行过程中必然会面临关闭或重启服务的某个实例的情况,在服务关闭期间,我们自然不希望客户端会继续调用关闭了的实例。所以在客户端程序中,当服务实例进行正常的关闭操作时,它会触发一个服务下线的REST请求给Eureka Server,告诉服务注册中心:“我要下线了”。服务端在接收到请求之后,将该服务状态置为下线(DOWN),并把该下线事件传播出去。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/314576.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号