目录javascript
Spring cloud eureka
是Spring cloud netfilx
中的一部分,它基于Netflix Eureka
作了二次封装,主要职责完成Eureka 中的服务治理功能java
本篇主要探讨以下:web
服务治理能够是说微服务架构中最为核心的基础模块,它主要用来实现各个微服务实现的自动化注册与发现。在开始的时候微服务系统的服务可能并很少,咱们须要一些配置来完成服务的调用。spring
服务注册
: 在服务治理框架中,一般会构建一个注册中心,由各个服务提供者来向注册中心登记并提供服务,将主机与端口号、版本号、通讯协议等一些附加信息告知注册中心,注册中心按照服务名分类组织服务清单。apache
服务名 | 位置 |
---|---|
服务A | 192.168.1.101:8000, 192.168.1.102:8000 |
服务B | 192.168.1.103:9000,192.168.1.104:9000,192.168.1.105:9000 |
好比咱们有两个提供服务A 的进程分别位于192.168.1.101:8000, 192.168.1.102:8000 上,另外还有三个提供服务B 的进程分别位于192.168.1.103:9000,192.168.1.104:9000,192.168.1.105:9000 进程上,那么你向服务中心注册事后,服务中心就会有一个这样的服务列表,服务中心向各个注册的服务发送心跳机制,来检验服务是否可用,若不可用就会把服务剔除,来达到故障排除的效果。缓存
服务发现
: 因为在服务治理框架下运做,服务间的调用再也不经过指定的Ip:端口号
这种方式来实现 ,而是向服务名发起请求实现。因此,在服务调用方在调用服务提供方接口的时候,并不知道具体服务的位置。所以,服务调用方须要向服务中心
获取服务列表,以实现对具体服务
的访问。网络
好比一个服务调用者C想要获取服务A的ip来完成接口的调用,那么他首先应该去服务中心发起咨询你服务的请求,由注册中心的服务列表将A的位置发送给调用者C,若是按照上面服务A地址的话,那么调用者C会由两个服务A的地址来提供服务,当服务C须要调用的时候,便从服务A中的清单中采用轮询的方式取出一个位置来服务调用,这个过程也被称为负载均衡。架构
Eureka
是Netflix开发的服务发现框架
,自己是一个基于REST的服务
,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。Eureka
包含两个组件: Eureka Server
和 Eureka Client
Eureka Server
简称Eureka 服务端, 主要提供服务注册功能,其实也就至关因而注册中心,和其余服务注册中心同样,提供高可用的配置,同时也支持集群部署
,当集群中某一个节点发生故障时,那么Eureka就会进入自我保护模式,它容许故障的节点继续提供服务的发现与注册,当故障分片恢复运行时,集群中的其余分片会把他们的状态再同步回来。Eureka Client
:简称Eureka 客户端,主要处理服务的注册与发现。客户端经过注解和参数配置的方式,Eureka 客户端向注册中心注册自身的服务并周期性的发送心跳机制来更新服务租约。同时,它也能从服务端查询当前注册的服务信息并把它们缓存到本地并周期性地刷新服务状态。Spring Cloud Eureka 是采用SpringBoot 进行项目的快速搭建的,若是不太了解SpringBoot的话,能够了解一下SpringBoot 入门实例。app
Eureka服务端
,建立完成后在pom.xml文件中增长以下maven依赖,完整的文件以下:<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.eureka.server</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-server</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
@SpringBootApplication
修饰的主方法中加入以下注解@EnableEurekaServer
。@EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
加入这个注解也就标识着这是一个Eureka的服务端,能够启动服务了,可是启动服务会报错,由于你没有添加注册中心的相关配置。负载均衡
application.properties
文件中加入以下内容server.port=8000 eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.service-url.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/
server.port 就表明着注册中心的端口号
eureka.client.service-url.defaultZone :eureka客户端默认服务url
eureka.client.register-with-eureka : 表示注册中心是否向其余注册中心注册本身,单节点注册中心不须要,设置为false
eureka.client.fetch-registry: 表示注册中心是否主动去检索服务,并不须要检索服务,设置为false
其余配置:
# 项目contextPath,通常在正式发布版本中,咱们不配置 # 避免加上更目录:Cannot execute request on any known server # 加上根目录也须要在注册地址上加入根 server.context-path=/eureka81 # 错误页,指定发生错误时,跳转的URL。请查看BasicErrorController源码便知 server.error.path=/error # 经过spring.application.name属性,咱们能够指定微服务的名称后续在调用的时候只须要使用该名称就能够进行服务的访问。 spring.application.name=eureka-server # eureka是默认使用hostname进行注册,可经过一下项自动获取注册服务IP或者直接经过eureka.instance.ip-address指定IP # eureka.instance.prefer-ip-address=true # SpringBoot 在启动的时候会读配置文件,会把prefer-ip-address 默认转换为preferIpAddress驼峰命名 eureka.instance.preferIpAddress=true # 设为false,关闭自我保护 eureka.server.enable-self-preservation=false # 清理间隔(单位毫秒,默认是60*1000 eureka.server.eviction-interval-timer-in-ms=6000 # 开启健康检查(须要spring-boot-starter-actuator依赖) eureka.client.healthcheck.enabled=false # 续约更新时间间隔(默认30秒) eureka.instance.lease-renewal-interval-in-seconds=10 # 续约到期时间(默认90秒) eureka.instance.lease-expiration-duration-in-seconds=30
没有加入 eureka.instance.preferIpAddress=true
以前,默认本地为注册中心
加入 eureka.instance.preferIpAddress=true
以后,圈出来的ip即为eureka.client.service-url.defaultZone指定的 ip。
在完成了上述配置以后,应用程序启动并访问http://localhost:1111/ 能够看到以下图所示的信息版,其中Instances curently registered with Eureka 是空的,代表尚未任何服务提供者提供服务。
在完成了上述搭建以后,接下来咱们尝试将一个既有的SpringBoot应用加入Eureka服务治理体系去。
使用上一小节的快速入门工程进行改造,将其做为一个微服务应用向服务注册中心发布注册本身
pom.xml配置以下:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
pom.xml
,咱们须要在启动类上加入@EnableDiscoverClient
注解,用于开启eureka-client客户端application.properties
中加入以下内容# 这个名字就是Eureka注册中新的实例名称 spring.application.name=server-provider # 向注册中心注册本身 eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
@RestController public class HelloController { private final Logger log = LoggerFactory.getLogger(HelloController.class); @Resource private DiscoveryClient discoveryClient; @RequestMapping(value = "hello", method = RequestMethod.GET) public String hello(){ ServiceInstance instance = discoveryClient.getLocalServiceInstance(); log.info("instance.host = " + instance.getHost() + " instance.service = " + instance.getServiceId() + " instance.port = " + instance.getPort()); return "Hello World"; } }
eureka-provider
注册到了注册中心此处的Status 中的内容也就包括上面配置的spring.application.name=server-provider
在主页访问 http://localhost:8080/hello ,发现页面上 输出了Hello World
,控制台打印出来了
c.s.provider.controller.HelloController : instance.host = macliu instance.service = server-provider instance.port = 8080
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
这是Eureka的一种自我保护机制,Eureka Server在运行期间,会统计心跳失败的比例在15分钟以内是否低于85%,若是出现低于的状况(在单机调试的时候很容易知足,实际在生产环境上一般是因为网络不稳定致使),Eureka Server会将当前的实例注册信息保护起来,同时提示这个警告。
Eureka server和client之间每隔30秒会进行一次心跳通讯,告诉server,client还活着
这个表示server-provider 已经标记为下线,也就是 DOWN 状态,再次从新上线后,发现Status又变为了UP状态。
在微服务架构这样的分布式环境中,须要充分考虑到发生故障的状况,因此在生产环境中必须对各个组件进行高可用部署,对于微服务是如此,对于注册中心也同样。
Eureka Server
的设计就充分考虑了高可用问题,在Eureka的服务治理体系中,全部的节点既是服务提供方,也是服务的消费者,服务注册中心也不例外,不一样的注册中心在向其余注册中心提供节点列表的时候,也在向其余注册中心获取节点列表。
高可用的配置中心就是向其余注册中心注册本身,同时把服务列表提供给其余注册中心,从而达到注册中心列表同步,达到高可用的效果。经过下面两个配置来实现
eureka.client.register-with-eureka=true eureka.client.fetch-registry=true
下面就在单节点的基础之上建立一下高可用
的配置中心(双节点注册中心
)
首先,建立两个配置文件,分别是application-peer1.properties
和 application-peer2.properties
,内容分别以下
application-peer1.properties
spring.application.name=eureka-server server.port=1111 eureka.instance.hostname=peer1 eureka.client.register-with-eureka=true eureka.client.fetch-registry=true eureka.client.service-url.defaultZone=http://peer2:1112/eureka/
application-peer2.properties
spring.application.name=eureka-server server.port=1112 eureka.instance.hostname=peer2 eureka.client.register-with-eureka=true eureka.client.fetch-registry=true eureka.client.service-url.defaultZone=http://peer1:1111/eureka/
在本地修改配置文件/etc/hosts ,Windows下面是C:\Winows\System32\drivers\etc\hosts。
添加以下内容
127.0.0.1 peerl 127.0.0.1 peer2
以下:
首先在idea 或者eclipse 使用mvn clean 和 mvn install命令,会直接打包,这里注意,必定要在pom.xml中配置以下,不然使用java -jar
会报没有主清单属性的错误。
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
打包完成后,切换到eureka-server
项目,再切换到target目录下,此时有mvn install 的jar包,使用java -jar eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1
和 java -jar eureka-server-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2
两个命令,启动两个例程。起来事后分别访问 http://localhost:peer1/eureka/ 和 http://localhost:peer2/eureka/ 主页,发现对应的注册中心分别注册进去了,并且分片也处于可用分片状态。
到如今为止,咱们已经让两个注册中心分别注册各自的服务了,还记得上面还有一个server-provider
服务吗?咱们也让server-provider分别注册到这两个注册中心。
在server-provider
中修改对应的配置文件
eureka.client.service-url.defaultZone=http://peer1:1111/eureka, http://peer2:1112/eureka/
启动程序,发现http://localhost:1111/ 和 http://localhost:1112/ 中都注册了server-provider
服务
访问http://localhost:8080/hello,你会发现页面上显示出来
hello world
,断开其中的任意一个注册中心,hello world
也可以显示出来。也就是说,server-provider 分别对两个注册中心分别注册了各自的服务,由两个注册中心以轮询的方式提供服务。断开其中一个注册中心,还有另一个注册中心能够提供服务,这也是Eureka 高可用的体现。
注意事项
application-peer1.properties
和 application-peer2.properties
中的eureka.instance.hostname
与 本地hosts文件中的名称不一致的话,那么注册中心启动后,会使分片处于不可用的状态, spring.application.name
表示的是实例的名称,也就是以下的地方当server-provider
注册进来的时候,高可用配置的注册中心会以轮询的方式提供服务,每次提供服务是哪一个注册中心是不可预知的。
如咱们不想使用主机名来定义注册中心的地址,也可使用IP地址的形式, 可是须要在配置文件中增长配置参数eureka.instance.prefer-ip-address=true, 该值默认为false。
经过上述的内容介绍与实践,咱们已经搭建起来微服务架构中的核心组件— 服务注册中心(包括单节点模式和高可用模式)。并用server-provider
注册双节点,在页面上发起一个url请求时,注册中心找到server-provider
,并有两个节点以轮询的方式提供服务。
下面就来构建一个消费者
,它主要完成两个目标:发现服务
和消费服务
。其中,服务发现的任务由Eureka
客户端完成,消费服务
的任务由Ribbon
来完成。
先来认识一下什么是Ribbon
:Ribbon是客户端负载均衡器
,可让您对HTTP和TCP客户端的行为进行控制。 Feign已经使用了Ribbon,若是你使用了@FeignClient
,那么Ribbon也适用。
Ribbon
能够在经过客户端中配置的ribbonServerList
服务端列表去轮询访问以达到负载均衡的效果。当ribbon
与Eureka
联合使用时,Ribbon
的服务实例清单RibbonServerList
会被DiscoveryEnabledNIWSServerList
重写,扩展成从Eureka注册中心中获取服务端列表。同时它也会用NIWSDiscoveryPing来取代Ping,它将职责委托给Eureka来肯定服务端是否启动,咱们目前不细致探讨Ribbon的细节问题。
下面经过一个简单的实例,看看Eureka的服务治理体系下如何实现服务的发现与消费。
eureka-server
以及server-provider
服务,为了实现ribbon的负载均衡功能,咱们经过java -jar
命令行的方式来启动两个不一样端口的server-provider
java -jar service-provider-0.0.1-SNAPSHOT.jar --server.port=8081
和 java -jar service-provider-0.0.1-SNAPSHOT.jar --server.port=8082
来启动两个server-provider 进程<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Brixton.SR5</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
@EnableDiscoveryClient
注解,让该注解注册为Eureka客户端,以得到服务发现的能力,同时,建立RestTemplate
对象,加上@LoadBalance
注解开启负载均衡。@EnableDiscoveryClient @SpringBootApplication public class RibbonConsumerApplication { @Bean @LoadBalanced RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RibbonConsumerApplication.class, args); } }
RibbonController
类,注入@RestTemplate
,构造一个方法来调用server-provider
中的/hello 方法。代码以下@RestController public class RibbonController { @Autowired RestTemplate restTemplate; @RequestMapping(value = "/ribbon-consumer",method = RequestMethod.GET) public String helloConsumer(){ return restTemplate.getForEntity("http://server-provider/hello",String.class).getBody(); } }
在helloConsumer方法上面采用Restful 风格的编码方式,这个方法远程调用了
server-provider
中的hello方法,在这里不像是http://ip:端口号这种书写方式,而是直接采用 服务名/方法名来直接调用方法,由于你不知道具体的方法在哪一个ip的机器上,须要由Eureka进行调用,这样消费者就不用关心由谁提供了服务,只要提供了服务便可,这也是面向对象方法的一种体现,同时也能很好的解耦。
application.properties
配置以下spring.application.name=ribbon-consumer server.port=9000 eureka.client.service-url.defaultZone=http://localhost:1111/eureka/
由于ribbon-consumer须要由客户端来主动调用方法,因此须要提供实例名称,端口号,并在注册中心注册ribbon-consumer服务
INFO 29397 --- [nio-9000-exec-1] c.n.l.DynamicServerListLoadBalancer : DynamicServerListLoadBalancer for client server-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=server-provider,current list of Servers=[macliu:8082, macliu:8081],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone; Instance count:2; Active connections count: 0; Circuit breaker tripped count: 0; Active connections per server: 0.0;] },Server stats: [[Server:macliu:8082; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] , [Server:macliu:8081; Zone:defaultZone; Total Requests:0; Successive connection failure:0; Total blackout seconds:0; Last connection made:Thu Jan 01 08:00:00 CST 1970; First connection made: Thu Jan 01 08:00:00 CST 1970; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:0.0; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:0.0; max resp time:0.0; stddev resp time:0.0] ]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@3dc2c2eb
再尝试刷新几回url,由于咱们实在后台启动的程序,在终端会看到以下的信息
INFO 28929 --- [nio-8082-exec-3] c.s.provider.controller.HelloController : instance.host = macliuinstance.service = server-providerinstance.port = 8082
由于开了两个终端,一个是8081
端口,一个是8082
端口,多刷新几回页面后,会发现终端在循环输出上面的信息,来判断使用ribbon 实现了负载均衡。
欢迎关注Java建设者:一块儿学习交流