文章目录
搭建服务注册中心 --Eureka
Eureka 简介
- Eureka 是 Netflix 出品的用于实现服务注册和发现的工具。 Spring Cloud 集成了 Eureka,并提供了开箱即用的支持。其中, Eureka 又可细分为 Eureka Server 和 Eureka Client。
上图是基于集群配置的eureka;
处于不同节点的eureka通过Replicate进行数据同步
Application Service为服务提供者
Application Client为服务消费者
Make Remote Call完成一次服务调用
Eureka原理
- 服务启动后向Eureka注册,Eureka Server会将注册信息向其他Eureka Server进行同步,当服务消费者要调用服务提供者,则向服务注册中心获取服务提供者地址,然后会将服务提供者地址缓存在本地,下次再调用时,则直接从本地缓存中取,完成一次调用。
- 当服务注册中心Eureka Server检测到服务提供者因为宕机、网络原因不可用时,则在服务注册中心将服务置为DOWN状态,并把当前服务提供者状态向订阅者发布,订阅过的服务消费者更新本地缓存。
- 服务提供者在启动后,周期性(默认30秒)向Eureka Server发送心跳,以证明当前服务是可用状态。Eureka Server在一定的时间(默认90秒)未收到客户端的心跳,则认为服务宕机,注销该实例
搭建一个父工程继承SpringBoot父工程和SpringCloud父工程
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.7.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qf.test</groupId>
<artifactId>springcloud_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud_demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
创建一个SpringBoot Web项目继承父工程
- POM
<parent>
<groupId>com.qf.test</groupId>
<artifactId>springcloud_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.qf.test</groupId>
<artifactId>springcloud_eureka_server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud_eureka_server</name>
<description>Demo project for Spring Boot</description>
<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-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 主启动类
@SpringBootApplication
@EnableEurekaServer
public class SpringcloudEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudEurekaServerApplication.class, args);
}
}
- 配置
#配置eureka
#关闭eureka自动注册服务,默认为true,如果不设置会把当前应用注册上去
eureka.client.register-with-eureka=false
#禁止当前微服务调用其他微服务
eureka.client.fetch-registry=false
#eureka注册中心地址,这个地址不要驼峰
eureka.client.service-url.defaultZone=http://localhost:8080/eureka
#配置应用程序的名称,不要使用下划线命名
spring.application.name=eureka-server
- 访问
http://localhost:8080
搭建微服务-提供者
- 创建一个SpringBooWeb项目继承父工程
- 自定义一个Controller
@RequestMapping(value = "/hello")
public class HelloController {
@RequestMapping(value = "/test1/{id}")
@ResponseBody
public String test1(@PathVariable Integer id){
System.out.println("HelloController.test1 id:"+id);
return "id:"+id;
}
}
因为SpringCloud是通过HTTP调用服务,所以这里要写一个Controller
- 配置
server.port=8081
#eureka服务中心地址
eureka.client.service-url.defaultZone=http://localhost:8080/eureka
# 微服务提供者名称
spring.application.name=eureka-provide
- 主启动类
@EnableEurekaServer
@SpringBootApplication(scanBasePackages = "com.qf")
public class EurekaProviderApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaProviderApplication.class, args);
}
}
- 测试
- 在eureka注册中心可以看到该微服务
- 可以直接在浏览器中调用微服务提供者的Controller
Eureka自我保护机制
-
eureka.server.enable-self-preservation=false(默认是true)
打开自我保护机制:注册中心不会自动移除不可用的微服务,Eureka会保护起来。
关闭自我保护机制: 注册中心会自动移除不可用的微服务 -
Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 会将这些实例保护起来,让这些实例不会过期,但是在保护期内如果服务刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等。
-
Eureka 的自我保护模式是有意义的,该模式被激活后,它不会从注册列表中剔除因长时间没收到心跳导致租期过期的服务,而是等待修复,直到心跳恢复正常之后,它自动退出自我保护模式。
-
这种模式旨在避免因网络分区故障导致服务不可用的问题。例如,两个客户端实例 C1 和 C2 的连通性是良好的,但是由于网络故障,C2 未能及时向 Eureka 发送心跳续约,这时候 Eureka 不能简单的将 C2 从注册表中剔除。因为如果剔除了,C1 就无法从 Eureka 服务器中获取 C2 注册的服务,但是这时候 C2 服务是可用的。
-
自我保护机制的触发条件
Renews threshold:期望每分钟收到客户端心跳数
Renews (last min):最后1分钟收到客户端实心跳数当Renews <Renews threshold * 0.85 就会触发自我保护机制
服务消费者–Ribbon
- 创建一个SpringBootWeb工程
- pom
<parent>
<groupId>com.qf.test</groupId>
<artifactId>springcloud_demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.qf.test</groupId>
<artifactId>eureka_consumer_ribbon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka_consumer_ribbon</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.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-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
- 主启动类
@EnableEurekaClient
@SpringBootApplication(scanBasePackages = "com.qf")
public class EurekaConsumerRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaConsumerRibbonApplication.class, args);
}
@Bean
@LoadBalanced // 负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
@Bean // 随机的负载均衡方式
public IRule getRule(){
return new RandomRule();
}
}
- 配置
server.port=8082
eureka.client.service-url.defaultZone=http://localhost:8080/eureka
spring.application.name=eureka-consumer-ribbon
- Controller
@Controller
@RequestMapping(value = "/consumer")
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/hello")
@ResponseBody
public String test(){
System.out.println("RibbonTest.test");
// 这种写法耦合了IP和端口,如果IP发生变化需要在这里修改代码
// String reuslt = restTemplate.getForObject("http://localhost:8081/provider/test/11", String.class);
// 这里是写的微服务的名称,需要RestTemplate上添加@LoadBalanced注解
String reuslt = restTemplate.getForObject("http://EUREKA-PROVIDER/provider/test/11", String.class);
return "[ribbon result ] "+reuslt;
}
}
- 负载均衡原理
服务消费者–Feign
- 面向对象的方式调用微服务,类似于Dubbo中的方式。
- 创建一个SpringBootWeb工程
- pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 主启动类
@EnableEurekaClient
@EnableFeignClients("com.qf.service")
@SpringBootApplication(scanBasePackages = "com.qf")
public class EurekaConsumerFeginApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaConsumerFeginApplication.class, args);
}
}
- 配置
server.port=8084
eureka.client.service-url.defaultZone=http://localhost:8080/eureka
spring.application.name=enreka-consumer-fegin
- Service
@FeignClient(name = "EUREKA-PROVIDE") // 调用服务的名称
@RequestMapping(value = "/provider") // 要和服务提供者保持一致
public interface HelloService {
@RequestMapping(value = "/test1/{id}")// 要和服务提供者保持一致
String test1(@PathVariable("id") Integer id);
}
@PathVariable中需要加属性名称
- Controller
@Controller
@RequestMapping(value = "consumerFegin")
public class HelloController {
@Autowired
private HelloService helloService;
@RequestMapping(value = "/test1")
@ResponseBody
public String test1(){
System.out.println();
return helloService.test1(10);
}
}
服务消费Ribbon和Feign区别
- Ribbon
Ribbon 是一个基于 HTTP 客户端的负载均衡器,从 Eureka 注册中心获取服务端列表。 - Feign
Feign 是在 Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。采用接口的方式, 只需要创建一个接口,然后在上面添加注解即可 ,将需要调用的其他服务的方法定义成抽象方法即可,不需要自己构建http请求。 - 总结起来就是:发布到注册中心的服务方接口是 HTTP的,也可以不用 Ribbon 或者 Feign,直接浏览器一样能够访问。只不过 Ribbon 或者 Feign 调用起来要方便一些,最重要的是:它俩都支持软负载均衡