文章目录
Spring cloud 简介
Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
Spring Cloud组成编辑
Spring Cloud的子项目,大致可分成两类,一类是对现有成熟框架”Spring Boot化”的封装和抽象,也是数量最多的项目;第二类是开发了一部分分布式系统的基础设施的实现,如Spring Cloud Stream扮演的就是kafka, ActiveMQ这样的角色。对于我们想快速实践微服务的开发者来说,第一类子项目就已经足够使用,如:
- Spring Cloud Netflix
是对Netflix开发的一套分布式服务框架的封装,包括服务的发现和注册,负载均衡、断路器、REST客户端、请求路由等。 - Spring Cloud Config
将配置信息中央化保存, 配置Spring Cloud Bus可以实现动态修改配置文件 - Spring Cloud Bus
分布式消息队列,是对Kafka, MQ的封装 - Spring Cloud Security
对Spring Security的封装,并能配合Netflix使用 - Spring Cloud Zookeeper
对Zookeeper的封装,使之能配置其它Spring Cloud的子项目使用 - Spring Cloud Eureka
Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件中的一部分,它基于Netflix Eureka 做了二次封装,主要负责完成微服务架构中的服务治理功能。
以前我们使用Spring boot 调用服务的时候直接在配置文件里面 将 Http 接口 配置好,然后读取注入到指定地方通过Http 工具类进行发送,调用返回数据。
而使用Spring cloud之后 ,Spring cloud 使用 eureka 作为服务的注册中心,将服务统一进行管理。
通过新建Spring boot项目,新建服务注册中心 eurekaserver 项目。
首先添加依赖配置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.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.eurekaserver</groupId>
<artifactId>eurekaserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eurekaserver</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<!-- springcloud 依赖管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
<exclusions>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 子功能依赖-->
<dependencies>
<!-- 添加eureka 服务依赖-->
<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-security</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yml
#tomcat端口
server:
port: 6868
spring:
application:
#指定服务名称
name: user_server
eureka:
client:
registerWithEureka: true#是否将自己注册到Eureka服务中,本身就是服务,所以无需注册
fetchRegistry: false # 是否从Eureka 中获取注册信息
serviceUrl: # Eureka 客户端与Eureka服务端进行交互的地主
defaultZone: http://127.0.0.1:6868/eureka/
instance:
#将自己的ip注册到eureka服务中
prefer-ip-address: true
服务续约详解(服务提供者)
上面没有配置服务发送的心跳检测(续约),默认不需要配置。
服务每隔30秒会向注册中⼼续约(⼼跳)⼀次(也称为报活),如果没有续约,租约在
90秒后到期,然后服务会被失效。每隔30秒的续约操作我们称之为⼼跳检测
配置的yml
eureka:
instance:
# 租约续约间隔时间,默认30秒
lease-renewal-interval-in-seconds: 30
# 租约到期,服务时效时间,默认值90秒,服务超过90秒没有发⽣⼼跳,服务注册中心会将服务从列表移除
lease-expiration-duration-in-seconds: 90
- 1)服务消费者启动时,从 EurekaServer服务列表获取只读备份,缓存到本地
- 2)每隔30秒,会重新获取并更新数据
- 3)每隔30秒的时间可以通过配置eureka.client.registry-fetch-interval-seconds修 改
获取服务列表详解(服务消费者)
每隔30秒服务会从注册中⼼中拉取⼀份服务列表,这个时间可以通过配置修改。基本不需要我们调整
#向Eureka服务中⼼集群注册服务
eureka:
client:
# 每隔多久拉取⼀次服务列表
registry-fetch-interval-seconds: 30
- 1)服务消费者启动时,从 EurekaServer服务列表获取只读备份,缓存到本地
- 2)每隔30秒,会重新获取并更新数据
- 3)每隔30秒的时间可以通过配置eureka.client.registry-fetch-interval-seconds修 改
服务下线
- 1)当服务正常关闭操作时,会发送服务下线的REST请求给EurekaServer。
- 2)服务中⼼接受到请求后,将该服务置为下线状态
失效剔除
Eureka Server会定时(间隔值是eureka.server.eviction-interval-timer-in-ms,默认60s)进⾏检查;
如果发现实例在在⼀定时间(此值由客户端设置的eureka.instance.lease-expiration-duration-in-seconds定义,默认值为90s)内没有收到⼼跳,则会注销此实例。
eureka 自我保护机制
服务提供者 -----> 注册中⼼
定期的续约(服务提供者和注册中⼼通信),假如服务提供者和注册中⼼之间的⽹络有点问题,不代表服务提供者不可⽤,不代表服务消费者⽆法访问服务提供者
如果在15分钟内超过85%的客户端节点都没有正常的⼼跳,那么Eureka就认为客户端与注册中⼼出现了⽹络故障,Eureka Server⾃动进⼊⾃我保护机制。
为什么会有⾃我保护机制?
默认情况下,如果Eureka Server在⼀定时间内(默认90秒)没有接收到某个微服务实例的⼼跳,Eureka Server将会移除该实例。但是当⽹络分区故障发⽣时,微服务与Eureka Server之间⽆法正常通信,⽽微服务本身是正常运⾏的,此时不应该移除这个微服务,所以引⼊了⾃我保护机制。
自我保护机制设置 示例
启动类添加注解
//开启服务中心,生命是一个eureka服务注册中心
@EnableEurekaServer
@SpringBootApplication
public class EurekaserverApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaserverApplication.class, args);
}
}
启动服务,打开 http://127.0.0.1:6868/ (自己端口号)
可以看到
说明eureka服务中心注册成功。
服务注册到eureka 服务中
同理新建服务 eurekaserver1
和上面依赖 ,注解,yml基本一致
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 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.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.eurekaserver</groupId>
<artifactId>eurekaserver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eurekaserver</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<!-- springcloud 依赖管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
<exclusions>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 子功能依赖-->
<dependencies>
<!-- 添加eureka 服务依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
main启动类
//声明是一个客户端
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaserverApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaserverApplication.class, args);
}
}
yml
唯一区别在于 yml中需要将自己注册到eureka服务注册中心里面
#tomcat端口
server:
port: 6869
spring:
application:
#指定服务名称
name: user_server
eureka:
client:
registerWithEureka: true #是否将自己注册到Eureka服务中
fetchRegistry: false # 是否从Eureka 中获取注册信息
serviceUrl: # Eureka 客户端与Eureka服务端进行交互的地主
defaultZone: http://127.0.0.1:6868/eureka/
instance:
#将自己的ip注册到eureka服务中
prefer-ip-address: true
然后启动注册中心服务,我们可以看到 6869 这个服务注册到6868这个服务注册中心了。
服务调用
给6869那个服务中添加一个 返回对象
User 实体
package com.example.eurekaserver.eurekaserver;
/**
* @description: user
* @author: Administrator
* @create: 2019-11-06 22:34
**/
public class User {
private int id;
private String name;
private String sex;
private String count;
public User(int id, String name, String sex, String count) {
this.id = id;
this.name = name;
this.sex = sex;
this.count = count;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getCount() {
return count;
}
public void setCount(String count) {
this.count = count;
}
}
controller
@RestController
public class UserContronller {
private static List<User> users = new ArrayList<>();
static {
users.add(new User(1,"name1","男","123user"));
users.add(new User(2,"name2","男","123user"));
users.add(new User(3,"name3","男","123user"));
users.add(new User(4,"name4","男","123user"));
users.add(new User(5,"name5","男","123user"));
users.add(new User(6,"name6","男","123user"));
}
@RequestMapping("getUser")
public User getUser(int id){
return new User(1,"name1","男","123user");
}
}
消费服务
新建 eurekaclient
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 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.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.eurekaclient</groupId>
<artifactId>eurekaclient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eurekaclient</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<!-- springcloud 依赖管理-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
<exclusions>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<!-- 子功能依赖-->
<dependencies>
<!-- 添加eureka 消费依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yml
eureka:
client:
registerWithEureka: false #是否将自己注册到Eureka服务中,本身就是服务,所以无需注册
fetchRegistry: true # 是否从Eureka 中获取注册信息
serviceUrl: # Eureka 客户端与Eureka服务端进行交互的地主
defaultZone: http://127.0.0.1:6868/eureka/
server:
port: 8080
main启动类
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaclientApplication {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(EurekaclientApplication.class, args);
}
}
controller
消费6869服务的 controller
package com.example.eurekaclient.eurekaclient;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Map;
/**
* @description: 消费user服务
* @author: Administrator
* @create: 2019-11-06 22:46
**/
@RestController
public class GetUserController {
@Autowired
RestTemplate restTemplate;
//服务发现对象
@Autowired
DiscoveryClient discoveryClient;
@RequestMapping("get")
public User getUser(int id){
//user微服务的名字 用来想此服务发送请求
//获取实例 通过服务id 也就是服务名字
List<ServiceInstance> instances = this.discoveryClient.getInstances("USER_SERVER");
if(instances.isEmpty()){
return null;
}
//这里获取一个实例,多个实例需要负载均衡,这里先不使用
ServiceInstance serviceInstance = instances.get(0);
//调用其他的服务需要其他服务ip 以及端口等信息,这些的信息被称为:标准元数据。可以使⽤DiscoveryClient 获取指定微服务的所有元数据信息
//标准元数据:主机名、IP地址、端⼝号等信息,这些信息都会被发布在服务注册表中,⽤于服务之间的调⽤。
String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/getUser?id=" + id;
System.out.println(url + " = " + url);
User forObject = restTemplate.getForObject(url, User.class);
return forObject;
}
}
测试
打开 http://127.0.0.1:8080/get?id=1
成功返回信息,格式是xml格式
修改返回格式为json
排除eureka xml 转换
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
测试
注
如果在另一个消费服务中调用在服务注册中心中注册的服务时候获取 (serviceInstance.getHost() )到的主机名是自己电脑名字
需要在yml中配置 , 将自己的ip注册到eureka 服务中去。
instance:
#将自己的ip注册到eureka服务中
prefer-ip-address: true