目录
1.什么是服务治理
在传统rpc远程调用中,服务与服务依赖关系,管理比较复杂,所以需要使用服务治理,管理服务与服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
2.服务注册与发现
在服务注册与发现中,有一个注册中心,当服务器启动的时候,会把当前自己服务器的信息 比如 服务地址通讯地址等以别名方式注册到注册中心上。 另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现rpc调用。
springcloud支持的注册中心:Eureka,Consual(go语言写的),zookeeper
dubbo常用的注册中心:zookeeper,Redis
3.SpringCloud集成Eureka注册中心
3.1.演示
3.1.1.案例需求
实现会员服务(提供者) springcloud-member 通过eureka注册中心 调用订单服务(消费者) springcloud-order
3.1.2.项目结构
springcloud-eureka-server 注册中心
springcloud-member 服务提供者
springcloud-order 服务消费者
3.1.3.所用注解
@EnableEurekaServer 表示开启EurekaServer注册中心
@EnableEurekaClient 启动eureka客户端
@LoadBalanced 让restTemplate在请求时拥有客户端负载均衡能力
3.2.搭建注册中心
1.创建springboot项目: springcloud-eureka-server
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itmayiedu</groupId> <artifactId>springcloud-eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <!-- 管理依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!--SpringCloud eureka依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> <!-- 注意: 这里必须要添加,否则各种依赖有问题 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
注意:springboot版本问题,我试了几个版本,启动eureka都报错,只有2.0.1才能正常启动。
3.application.yml
###服务端口号 server: port: 8100 eureka: instance: ###注册中心IP地址 hostname: 127.0.0.1 client: serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ ###因为自己是为注册中心,不需要自己注册自己 register-with-eureka: false ###因为自己是为注册中心,不需要检索服务 fetch-registry: false
4.启动类
package com.itmayiedu.springcloudeurekaserver; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @SpringBootApplication @EnableEurekaServer public class AppEureka { public static void main(String[] args) { SpringApplication.run(AppEureka.class, args); } }
@EnableEurekaServer 表示开启EurekaServer服务,开启注册中心
5.启动eureka,浏览器访问http://localhost:8100/
3.3.搭建服务提供者 springcloud-member
1.创建springboot项目: springcloud-member
2.pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itmayiedu</groupId> <artifactId>springcloud-member</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springcloud-member</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <!-- 管理依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot整合eureka客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <!-- 注意: 这里必须要添加, 否者各种依赖有问题 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
3.application.yml
###服务启动端口号 server: port: 8000 ###服务名称(服务注册到eureka名称) spring: application: name: app-itmayiedu-member ###服务注册到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka ###是否将服务注册到注册中心(是) register-with-eureka: true ###是否需要从eureka上获取注册信息(是) fetch-registry: true
4.MemberController
package com.itmayiedu.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MemberController { @RequestMapping("/getMember") public String getMember() { return "会员系统"; } }
5.启动类
package com.itmayiedu; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableEurekaClient @SpringBootApplication public class SpringcloudMemberApplication { public static void main(String[] args) { SpringApplication.run(SpringcloudMemberApplication.class, args); } }
@EnableEurekaClient:启动eureka客户端
6.启动member项目,浏览器访问注册中心地址http://localhost:8100/
注:注册中心里已经发现了member项目,项目名称是在member项目里的application.yml中定义的
右边的是服务接口的地址,我们可以访问member接口:
3.4..搭建服务消费者 springcloud-order
1.创建springboot项目: springcloud-order
2.pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itmayiedu</groupId> <artifactId>springcloud-order</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springcloud-order</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <!-- 管理依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot整合eureka客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> </dependencies> <!-- 注意: 这里必须要添加, 否者各种依赖有问题 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
3.application.yml
###服务启动端口号 server: port: 8001 ###服务名称(服务注册到eureka名称) spring: application: name: app-itmayiedu-order ###服务注册到eureka地址 eureka: client: service-url: defaultZone: http://localhost:8100/eureka ###是否将服务注册到注册中心(是) register-with-eureka: true ###是否需要从eureka上获取注册信息(是) fetch-registry: true
4.rest的接口调用方式
在springcloud中,有两种远程调用接口方式:
1)rest(springboot的web组件提供的),rest底层使用的是httpclient方式
2)fegin(springcloud提供的)
在rest中,有两种调用方式:
1)服务接口地址:这一种相当于直接调用接口,并没有通过euraka
2)服务别名(注册到eureka),这一种相当通过别名于从eureka中获取到对应的接口地址,再通过地址调用。使用rest通过别名方式调用接口,需要依赖ribbon负载均衡器,eureka已经集成了负载均衡器,需要通过@LoadBalanced开启负载均衡器
5.第一种,通过服务接口地址调用
OrderController
package com.itmayiedu.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class OrderController { @Autowired private RestTemplate restTemplate; @RequestMapping("/getOrder") public String getOrder() { // 有两种调用方式:一种是服务别名,一种是服务接口地址 String memberUrl = "http://127.0.0.1:8000/getMember"; String result = restTemplate.getForObject(memberUrl, String.class); System.out.println("会员服务调用订单服务,result:" + result); return result; } }
启动类:
package com.itmayiedu; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableEurekaClient @SpringBootApplication public class SpringcloudOrderApplication { public static void main(String[] args) { SpringApplication.run(SpringcloudOrderApplication.class, args); } // 解决restTemplate找不到问题:将restTemplate注入到springboot容器中 @Bean RestTemplate restTemplate() { return new RestTemplate(); } }
启动order项目,浏览器访问eureka:两个服务都已经注册到注册中心
访问order项目:
6.第二种,通过注册别名调用
OrderController
package com.itmayiedu.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController public class OrderController { @Autowired private RestTemplate restTemplate; @RequestMapping("/getOrder") public String getOrder() { // 有两种调用方式:一种是服务别名,一种是服务接口地址 //String memberUrl = "http://127.0.0.1:8000/getMember"; String memberUrl = "http://app-itmayiedu-member/getMember"; String result = restTemplate.getForObject(memberUrl, String.class); System.out.println("会员服务调用订单服务,result:" + result); return result; } }
启动类
package com.itmayiedu; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @EnableEurekaClient @SpringBootApplication public class SpringcloudOrderApplication { public static void main(String[] args) { SpringApplication.run(SpringcloudOrderApplication.class, args); } // 解决restTemplate找不到问题:将restTemplate注入到springboot容器中 @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
@LoadBalanced让restTemplate在请求时拥有客户端负载均衡能力,如果不加次注解,访问时会报错
启动order项目,访问order项目:
3.5.搭建 服务提供者 集群
刚才已经启动了一台member项目,端口是8000,现在再启动一台member项目,端口号是8010,演示ribbon的负载均衡效果
1.修改member项目的MemberController类,显示端口号
package com.itmayiedu.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MemberController { @Value("${server.port}") private String port; @RequestMapping("/getMember") public String getMember() { return "会员系统:" + port; } }
2.修改application.yml文件,再启动一台8010端口会员项目。
访问eureka发现member项目有两台了:
3.浏览器依次访问,发现轮流访问两台会员项目。此处采用的负载均衡策略是最基本的轮训策略。
3.6.eureka的自我保护机制
3.6.1.概念
1)eureka自我保护机制的两种角色:EurekaClient(注册客户端),EurekaServer (注册中心服务端)
2)为什么会产生Eureka自我保护机制:为了防止EurekaClient可以正常运行,但是与EurekaServer网络不通的情况下,EurekaServer不会对EurekaClient服务进行剔除
3)自我保护机制:默认情况下EurekaClient会定时向EurekaServer发送心跳包,如果EurekaServer在一定时间内没有收到EurekaClient发送的心跳包,便会直接从服务注册列表中剔除该服务(默认90s),但是在短时间内丢失了大量的服务实例心跳,这时候EurekaServer会开启自我保护机制,不会剔除该服务。
4)eureka的自我保护机制,为什么不会剔除服务呢?
为了防止EurekaClient是可以正常访问的,但是只是与EurekaServer网络访问不同,防止误剔除服务。
5)在什么环境下需要开启自我保护机制:生产环境下建议开启自我保护机制,本地环境下禁止自我保护机制。
6)如果服务真的宕机呢?本地调用应该重试机制,保护接口网络延迟幂等性,服务降级功能。
3.6.2.开启自我保护机制
基于上面的代码,开启一台eureka注册中心,一台服务消费者order,两台服务提供者member。
多次访问order项目,发现order项目调用member项目时,会依次访问两台不同端口号的member项目。
但是,我此时停掉一台服务提供者member项目,会发现多次访问order项目时,会轮流显示调通依然启动的member项目和因为另一台宕机导致的报错页面,这就是eureka的自我保护机制。当然,短时间内是可以这样的,如果超出了自我保护机制的时间,eureka会自动剔除掉宕机的那台提供者。
3.6.3.禁止自我保护机制
在注册中心 springcloud-eureka-server 项目中,修改application.yml文件
###服务端口号 server: port: 8100 ##定义服务名称 spring: application: name: app-itmayiedu-eureka eureka: instance: ###注册中心ip地址 hostname: 127.0.0.1 client: serviceUrl: ##注册地址 defaultZone: http://${eureka.instance.hostname}:8100/eureka/ ####因为自己是注册中心,是否需要将自己注册给自己的注册中心(集群的时候是需要是为true) register-with-eureka: false ###因为自己是注册中心, 不需要去检索服务信息 fetch-registry: false server: # 测试时关闭自我保护机制,保证不可用服务及时踢出 enable-self-preservation: false ##剔除失效服务间隔 eviction-interval-timer-in-ms: 2000
在服务提供者 springcloud-member 项目中,修改application.yml文件
###会员项目的端口号 server: port: 8000 ###服务别名----服务注册到注册中心名称 spring: application: name: app-itmayiedu-member eureka: client: service-url: ##### 当前会员服务注册到eureka服务地址 defaultZone: http://localhost:8100/eureka ### 需要将我的服务注册到eureka上 register-with-eureka: true ####需要检索服务 fetch-registry: true # 心跳检测检测与续约时间 # 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务 instance: lease-renewal-interval-in-seconds: 1 lease-expiration-duration-in-seconds: 2
配置表示:1秒钟发送一次心跳包,如果2秒钟没有发送,则eureka剔除该服务。
再次访问:依然发现轮流出现访问通和报错信息,这是因为 客户端的本地缓存机制,访问请求时,会先从客户端本地缓存中获取接口列表,当本地缓存时间过了之后,才会从注册中心获取新的接口列表,所以访问时需要考虑两个因素,一个是客户端本地缓存,另一个是注册中心的自我保护机制,当两个时间都到期之后,就不会访问已经宕机的服务器了。
3.7.Eureka集群
3.7.1.高可用注册中心
在微服务中,注册中心非常核心,可以实现服务治理,如果一旦注册出现故障的时候,可能会导致整个微服务无法访问,在这时候就需要对注册中心实现高可用集群模式。
3.7.2.Eureka高可用原理
默认情况下Eureka是让服务注册中心,不注册自己
###因为该应用为注册中心,不会注册自己
register-with-eureka: true
###不需要去注册中心上检索服务
fetch-registry: true
Eureka高可用实际上将自己作为服务向其他服务注册中心注册自己,这样就可以形成一组相互注册的服务注册中心,从而实现服务清单的互相同步,达到高可用效果。
3.7.3.搭建Eureka集群
1.我们需要搭建两台eureka注册中心,一个是8100(刚才的),一个是9100(新建的)
2.注册中心端口号为8100的application.yml文件
###服务端口号 server: port: 8100 ###eureka 基本信息配置 spring: application: name: eureka-server eureka: instance: ###注册到eurekaip地址 hostname: 127.0.0.1 client: serviceUrl: defaultZone: http://127.0.0.1:9100/eureka/, http://127.0.0.1:9100/eureka/ ###因为自己是为注册中心,做集群时需要将自己注册到注册中心 register-with-eureka: true ###因为自己是为注册中心,做集群时需要检索服务 fetch-registry: true
注意:
1)多台eureka的名称必须相同
2)此eureka必须注册到别的集群上去
如果是三台,则需要将此台注册到另两台上去
3)必须开启自我注册和自我搜索
3.注册中心端口号为9100的application.yml文件
###服务端口号 server: port: 9100 ###eureka 基本信息配置 spring: application: name: eureka-server eureka: instance: ###注册到eurekaip地址 hostname: 127.0.0.1 client: serviceUrl: defaultZone: http://127.0.0.1:8100/eureka/ ###因为自己是为注册中心,不需要自己注册自己 register-with-eureka: true ###因为自己是为注册中心,不需要检索服务 fetch-registry: true
4.分别访问两台eureka,发现彼此都已经注册到对方注册中心的列表中了。
5.客户端注册eureka集群
member项目和order项目,只需要在application.yml文件中添加多个集群地址就可以了
6.我们分别访问两台eureka集群,发现一台有接口列表,另一台没有。
因为在注册过程中,只会保证有一台注册中心服务有对应的服务信息列表,当一台注册中心宕机后,会自动地转移到别的注册中心上去,当然,这种转移是需要一定时间的。
4.SpringCloud集成Zookeeper注册中心
4.1.项目结构
springcloud-member
springcloud-order
直接使用zookeeper软件,不搭建注册中心项目
4.2.搭建服务提供者 springcloud-member
1.创建springboot项目:springcloud-member
2.pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itmayiedu</groupId> <artifactId>springcloud-member</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springcloud-member</name> <description>Demo project for Spring Boot</description> <!-- 管理依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot整合zookeeper客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> </dependency> </dependencies> <!-- 注意: 这里必须要添加, 否者各种依赖有问题 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
3.application.yml
###订单服务的端口号 server: port: 8080 ###服务注册到注册中心的名称 spring: application: name: zk-member ###注册中心的地址 cloud: zookeeper: connect-string: 127.0.0.1:2181
4.MemberController
package com.itmayiedu.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @EnableDiscoveryClient @SpringBootApplication @RestController public class MemberController { @Value("${server.port}") private String port; @RequestMapping("/getMember") public String getMember(){ return "会员服务!"+ port; } public static void main(String[] args) { SpringApplication.run(MemberController.class, args); } }
@EnableDiscoveryClient 当启动zookeeper,consul注册中心客户端时,使用此注解
5.先启动zookeeper,再启动member项目,打开zookeeper可视化工具,发现member项目已经注册到注册中心上了
注:服务注册名称是持久节点,服务注册地址是临时节点
6.访问member接口
7.再启动一台8081端口号的member项目,发现有两台member项目注册到zookeeper中了
zookeeper与eureka不同的地方时:zookeeper没有自我保护机制,eureka有自我保护机制
4.3.搭建服务消费者 springcloud-order
1.创建springboot项目:springcloud-order
2.pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itmayiedu</groupId> <artifactId>springcloud-order</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springcloud-order</name> <description>Demo project for Spring Boot</description> <!-- 管理依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- SpringBoot整合zookeeper客户端 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId> </dependency> </dependencies> <!-- 注意: 这里必须要添加, 否者各种依赖有问题 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
3.application.yml
###服务端口号 server: port: 8090 ###服务名称 spring: application: name: zk-order cloud: zookeeper: ###注册到zookeeper地址 connect-string: 127.0.0.1:2181
4.OrderController
package com.itmayiedu.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @SpringBootApplication @RestController @EnableDiscoveryClient public class OrderController { @Autowired private RestTemplate restTemplate; @RequestMapping("/orderToMember") public String orderToMember() { String memberUrl = "http://zk-member/getMember"; return restTemplate.getForObject(memberUrl, String.class); } public static void main(String[] args) { SpringApplication.run(OrderController.class, args); } // 默认rest方式开启 负载均衡功能 如果以app-itmayiedu-member名称进行调用服务接口的时候 必须 @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
5.启动order项目,访问order接口:依次访问member集群
6.介绍一个关于注册中心的api,可以获取到注册中心上服务列表信息:DiscoveryClient
package com.itmayiedu.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import java.util.List; @SpringBootApplication @RestController @EnableDiscoveryClient public class OrderController { @Autowired private RestTemplate restTemplate; @Autowired private DiscoveryClient discoveryClient; @RequestMapping("/discoveryClientMember") public List<ServiceInstance> discoveryClientMember() { List<ServiceInstance> instances = discoveryClient.getInstances("zk-member"); for (ServiceInstance serviceInstance : instances) { System.out.println("url:" + serviceInstance.getUri()); } return instances; } @RequestMapping("/orderToMember") public String orderToMember() { String memberUrl = "http://zk-member/getMember"; return restTemplate.getForObject(memberUrl, String.class); } public static void main(String[] args) { SpringApplication.run(OrderController.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
5.SpringCloud集成Consul注册中心
5.1.Consul简介
Consul 是一套开源的分布式服务发现和配置管理系统,由 HashiCorp 公司用 Go 语言开发。
它具有很多优点。包括: 基于 raft 协议,比较简洁; 支持健康检查, 同时支持 HTTP 和 DNS 协议 支持跨数据中心的 WAN 集群 提供图形界面 跨平台,支持 Linux、Mac、Windows
Consul下载地址https://www.consul.io/downloads.html
5.2.Consul环境搭建
1.下载压缩包,解压之后,得到一个consul.exe文件
2.在当前目录下,执行cmd命令
启动consul命令: consul agent -dev -ui -node=cy
-dev开发服务器模式启动,-node结点名为cy,-ui可以用界面访问,默认能访问。
3.启动成功后,访问consul界面: http://localhost:8500
4.consul就这样简单的搭建成功了。我们可以制作一个bat文件启动consul。
在consul.exe同级目录下,创建一个bat文件,内容是启动consul命令: consul agent -dev -ui -node=cy
5.3.搭建服务提供者 springcloud-consul-member
1.创建springboot项目:springcloud-consul-member
2.pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.itmayiedu</groupId> <artifactId>springcloud-consul-member</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springcloud-consul-member</name> <description>Demo project for Spring Boot</description> <!-- 管理依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.M7</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- SpringBoot整合Web组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--SpringCloud 集成consul注册中心 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> </dependencies> <!-- 注意: 这里必须要添加, 否者各种依赖有问题 --> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
3.application.yml
###服务端口号 server: port: 8502 spring: ### 服务名称 application: name: consul-member ####consul注册中心地址 cloud: consul: ### consul地址 host: localhost ### ### consul端口号 port: 8500 discovery: ##服务地址自定义为为ip地址 hostname: 192.168.18.220 ###默认情况下 服务注册到注册中心 地址随机生成英文 pc-yushengjun:
4.ConsulMemberController
package com.itmayiedu.controller; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @SpringBootApplication @EnableDiscoveryClient public class ConsulMemberController { @RequestMapping("/getMember") public String getMember() { return "会员服务"; } public static void main(String[] args) { SpringApplication.run(ConsulMemberController.class, args); } }
5.启动consul,启动member项目,访问consul界面
6.总结:
其实我们可以发现,换不同注册中心的时候,无论是eureka,zookeeper,还是consul,接口调用方式都不变,不同的是启动注解,配置文件和maven依赖信息
@EnableDiscoveryClient 与@EnableEurekaClient区别
1)@EnableDiscoveryClient注解是基于spring-cloud-commons依赖,并且在classpath中实现; 适合于consul、zookeeper注册中心
2)@EnableEurekaClient注解是基于spring-cloud-netflix依赖,只能为eureka作用;
Zookeeper与Eureka区别
CPA理论:一个分布式系统不可能同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。在此Zookeeper保证的是CP, 而Eureka则是AP。
Consistency(一致性), 数据一致更新,所有数据变动都是同步的
Availability(可用性), 好的响应性能
Partition tolerance(分区容忍性) 可靠性
1、“C”是指一致性,即当一个Process(过程)修改了某个数据后,其他Process读取这是数据是,得到的是更新后的数据,但并不是所有系统都 可以做到这一点。例如,在一些并非严格要求一致性的系统中,后来的Process得到的数据可能还是修改之前的数据,或者需要等待一定时间后才能得到修改 之后的数据,这被成为“弱一致性”,最经典的应用就是DNS系统。当用户修改了DNS配置后,往往不会马上在全网更新,必定会有一个延迟,这个延迟被称为 “不一致窗口”,它的长度取决于系统的负载、冗余的个数等因素。但对于某些系统而言,一旦写入,后面读取的一定是修改后的数据,如银行账户信息,这被称为 “强一致性”。
2、“A”是指可用性。即系统总是能够为用户提供连续的服务能力。当用户发出请求是,系统能给出响应(成功或者失败),而且是立即给出响应,而不是等待其他事情完成才响应。如果需要等待某件事情完成才响应,那么“可用性”就不存在了。
3、“P”是指容错性。任何一个分布式计算系统都是由多个节点组成的。在正常情况下,节点与节点之间的通信是正常的。但是在某些情况下,节点之间的通信会 断开,这种断开成为“Partition”。在分布式计算的实现中,Partition是很常见的,因为节点不可能永远不出故障,尤其是对于跨物理地区的 海量存储系统而言,而容错性则可以保证如果只是系统中的部分节点不可用,那么相关的操作仍旧能够正常完成。
Zookeeper是保证CP
当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30 ~ 120s, 且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
Eureka是保证AP
Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
3. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。