SpringCloud实战之Eureka
- SpringCloud 介绍
- SpringCloud Netflix 组件
- SpringCloud 原生组件
- Eureka 使用
- 搭建Eureka Server
- 将服务提供者注册到注册中心
- 服务消费者通过注册中心获取服务列表并调用
- Demo存在的问题
- 9005 server:
- 9006 server:
- 重构之前的服务提供者与服务消费者
- 运行结果
- 两台Eureka运行结果
- 服务消费者运行结果
- 模拟9005宕机
- 服务消费者运行结果
- 细节处理
- 控制台上显示服务ip和端口
- eureka服务踢除
- eureka自我保护机制
- 总结
在
微服务架构整理-(四、无框架搭建微服务)中介绍了不使用框架的情况搭建一个微服务,但存在很多问题,本文引入SpringCloud框架相关组件对其进行重构,主要是通过Eureka对服务进行注册和发现,关于Eureka的介绍请参考
微服务架构整理-(三、注册中心之Eureka)。在重构之前对SpringCloud作一个简单的介绍。
SpringCloud 介绍
它是一系列框架的有序集合,它利用SpringBoot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、负载均衡、断路器、数据监控等,都可以用SpringBoot的开发风格做到一键启动和部署。Spring Cloud并没有重复造轮子,它只是将目前各家公司开发的比较成熟 ,经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂,易部署和易维护的分布式系统开发工具包。针对Springcloud很多公司都为其定制了相应的组件。
SpringCloud Netflix 组件
组件名称 | 作用 |
Eureka | 服务注册中心 |
Ribbon | 客户端负载均衡 |
Feign | 声明式服务调用 |
Hystrix | 客户端容错保护 |
Zuul | API服务网关 |
SpringCloud 原生组件
组件名称 | 作用 |
Consul | 服务注册中心 |
Config | 分布式配置中心 |
Sleuth/Zipkin | 分布式链路追踪 |
Gateway | API服务网关 |
Eureka 使用
搭建Eureka Server
为了方便写博,将之前的工程spring_cloud_demo重命名为spring_cloud_demo_1 ,接下来工程名上右击New->Module->maven,输入artifact_id:eureka_service。
1.
- 项目结构
其中,启动类中需要加上注解@EnableEurekaServer
如下所示:
package com.research.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author :TS六道轮回
* @date :Created in 2021/3/10 21:27
* @description:${description}
*/
@SpringBootApplication
//激活EurekaServer
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
- POM.xml
<?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">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>cn.research</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka_service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
- 配置文件
这里提供基本配置,其他配置逐渐引进
server:
port: 9005 #端口
# 配置eureka server
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false #是否将自己注册到注册中心,这里没有必要,所以false
fetch-registry: false #自己是否从注册中心获取注册信息,这里没有必要,所以false
service-url: #配置暴露给Eureka Client的请求地止
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
- 启动,浏览器输入localhost:9005, 结果如下:
到这,实现基本功能的Eureka就搭建完了,但这里有个坑,一定要注意,SrpingClout与SpringBoot的版本要匹配上,不然会出现可种奇怪的问题,具体可参考:
将服务提供者注册到注册中心
按照如下步骤重构之前的product_service:
- 添加EurekaClient依赖
<?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">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>cn.research</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>product_service</artifactId>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--Eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
- 添加EurecaServer相关配置信息
server:
port: 9001 #端口
spring:
favicon:
enabled: false
application:
name: service-product #服务名称
datasource:
username: root
password: 111111
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=UTF-8
jpa:
database: MySQL
show-sql: true
open-in-view: true
#配置eureka server
eureka:
client:
register-with-eureka: true
fetch-registry: false
service-url:
#eureka_service配置文件中的defaultZone
defaultZone: http://localhost:9005/eureka/
instance:
prefer-ip-address: true #使用ip地址进行注册
- 启动类中激活EurekaClient
package com.research.product;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author :TS六道轮回
* @date :Created in 2021/3/6 22:51
* @description:${description}
*/
@SpringBootApplication
@EntityScan("com.research.product.entity")
//激活EurekaClient
@EnableEurekaClient
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class,args);
}
}
- 启动produce 服务,在注册中心中可以看到相应的服务
服务消费者通过注册中心获取服务列表并调用
服务提供者注册后,它的相关的信息都存在于注册中心,这些信息通常称之为元数据,例如服务的主机名,ip等信息,服务消费者通过注册中心可以获取这些信息,从而达到与服务提供者交互的的作用。
按照如下步骤重构之前的order_service:
- 添加EurekaClient依赖
<?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">
<parent>
<artifactId>spring_cloud_demo</artifactId>
<groupId>cn.research</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>order_service</artifactId>
<dependencies>
<!--Eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
- 添加EurecaServer相关配置信息
server:
port: 9002 #端口
spring:
favicon:
enabled: false
application:
name: service-order #服务名称
#配置eureka server
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
#eureka_service配置文件中的defaultZone
defaultZone: http://localhost:9005/eureka/
instance:
prefer-ip-address: true #使用ip地址进行注册
- 启动类中激活EurekaClient
package com.research.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author :TS六道轮回
* @date :Created in 2021/3/9 21:32
* @description:${description}
*/
@SpringBootApplication
//激活EurekaClient
@EnableEurekaClient
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
- Controller中获取相应的元数据,并建立交互
package com.research.order.controller;
import com.research.order.dto.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author :TS六道轮回
* @date :Created in 2021/3/9 21:48
*/
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
RestTemplate restTemplate;
@Autowired
DiscoveryClient discoveryClient;
@GetMapping(value = "/buy/{id}")
public Product findById(@PathVariable Long id) {
//获取元数据
List<ServiceInstance> instances = discoveryClient.getInstances("service-product");
ServiceInstance instance = instances.get(0);
Product product = null;
//真正的开发过程这部分代码应该放到service层里
//获取ip和port拼接请求url
product = restTemplate.getForObject("http://"+instance.getHost()+":"+instance.getPort()+"/product/"+id,Product.class);
return product;
}
}
- 运行结果
到这,基于Eureka的微服务demo就搭建完了,但这只是万里长征第一步。
Demo存在的问题
假如Eureka注册中心宕机,整个架构就无法工作,因此这里就涉及到注册中心的可靠性问题。但根据CAP原理,Eureka本身遵循CP,不遵循A,所以为了提高Eureka的可靠性,可以设置两个Eureka server结点,当一个宕机,另一个立马开始工作。服务提供者和服务消费者需要注册到两Eureka结点上,并且要保证两个结点之间要相互注册,这样才能互为主备。架构图如下所示:
继续重构上面的工程,创建一个eureka-server-1的module, 步骤和之前创建eureka server基本一致,只是需要将配置文件进行修改。两个Eureka server 端口分别为9005,9006,对应的配置文件分别如下:
9005 server:
server:
port: 9005 #端口
spring:
application:
name: service-register #服务名称
# 配置eureka server
eureka:
client:
service-url: #将自己注册到9006对就的注册中心上
defaultZone: http://127.0.0.1:9006/eureka/
9006 server:
server:
port: 9006 #端口
spring:
application:
name: service-register-1 #服务名称
# 配置eureka server
eureka:
client:
service-url: #将自己注册到9005对就的注册中心上
defaultZone: http://127.0.0.1:9005/eureka/
重构之前的服务提供者与服务消费者
保证两者的可靠性,就得将他们分别注册到9005和9006上,配置文件中加入9006的地址即可:
#其他部分略。。。
eureka:
client:
register-with-eureka: true
fetch-registry: false
service-url:
#注册到两个eureka上
defaultZone: http://localhost:9005/eureka/,http://localhost:9006/eureka/
运行结果
两台Eureka运行结果
服务消费者运行结果
模拟9005宕机
接下来测试可靠性,关闭9005,运行结果如下:
服务消费者运行结果
细节处理
控制台上显示服务ip和端口
以服务提供者为例,在eureka的配置中添加instance-id,例如:
eureka:
client:
register-with-eureka: true
fetch-registry: false
service-url:
#注册到两个eureka上
defaultZone: http://localhost:9005/eureka/,http://localhost:9006/eureka/
instance:
prefer-ip-address: true #使用ip地址进行注册
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 向注册中心中注册服务id
结果如下:
eureka服务踢除
注册的服务默认每隔30秒向eureka发送一次心跳,如果90s内没有发送心跳,则此服务已宕机。再此以服务提供者为例,在eureka的配置中添加lease-renewal-interval-in-seconds,lease-expiration-duration-in-seconds 例如:
eureka:
client:
register-with-eureka: true
fetch-registry: false
service-url:
#注册到两个eureka上
defaultZone: http://localhost:9005/eureka/,http://localhost:9006/eureka/
instance:
prefer-ip-address: true #使用ip地址进行注册
instance-id: ${spring.cloud.client.ip-address}:${server.port} # 向注册中心中注册服务id
lease-renewal-interval-in-seconds: 5 #每5秒发送一次心跳
lease-expiration-duration-in-seconds: 10 #10秒内没有心跳,则踢除服务
结果如下:
eureka自我保护机制
上面将product服务踢除时间设为10s,即10s内如果product没有向eureka发出心跳,则可以踢除此服务,但是这种方法太不安全,万一是网络问题造成心跳包丢失而导致服务被踢除,难免有点粗暴,所以eureka本身有一个自我保护机制,即一定时间内,收不到某服务心跳的比例达到一定值时,则认为可以踢除此服务,是依据一个统计值来决定是否踢除服务。当自我保护机制开启时,某个服务达到一点的比例则踢除。当自我保护机制关闭时,则不会踢除服务,开发和测试过程建议关闭,通过在eureka-server的配置文件中添加enable-self-preservation即可。例如:
eureka:
client:
service-url: #将自己注册到9006对就的注册中心上
defaultZone: http://127.0.0.1:9006/eureka/
server:
enable-self-preservation: false #关闭自我保护
这一块个人觉得了解就好,没必要深入研究,大多数情况下使用默认值即可。
总结
基本eureka搭建微服务主要方面基本就介绍完了,其中包括服务注册,服务发现,可靠性,ip显示,服务踢除,自我保持机制,希望能帮助大家