SpringCloud–Eureka注册中心的使用
1.Eureka介绍
-
Eureka是什么
Eureka是基于REST(Representational State Transfer)服务,主要以AWS云服务为支撑,提供服务发现并实现负载均衡和故障转移。
-
Eureka相关概念
Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。 而系统中的其他微服务,使用 Eureka Client作为客户端连接到 Eureka Server并维持心跳连接。
Register:服务注册
当Eureka Client向Eureka Server注册时,它向Server提供自身的元数据(IP、port,主页、运行状态等)。
Renew :服务续约(心跳机制)
EurekaClient每30s发送一次心跳续约,告诉Eureka Server,客户仍存在并无异常问题。
Fetch Registeries:获取注册列表信息
EurekaClient从EurekaServer获取注册表中的信息,缓存到本地,并使用这些信息查找其他服务,从而实现远程调用。注册列表信息30s一更新,Eureka获取到不同于缓存的注册列表信息后会自动处理。当某些原因导致无法及时匹配注册列表信息,就会重新获取整个注册列表信息。
EurekaClient与EurekaServer之间使用JSON/XML方式进行通讯,并且默认使用压缩JSON格式获取注册表信息。
Cancel:服务下线
EurekaClient下线时向EurekaServer发送取消请求,Server将Client实例从注册列表删除。下线需调用 DiscoveryManager.getInstance().shutdownComponent();
Eviction:服务剔除
默认情况下,EurekaServer若90s没有收到Client的续约,Server将会把实例从注册表中删除。
-
Eureka的自我保护机制(模式)
官方对于自我保护机制的定义:
自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。
自我保护机制详细描述
在15分钟内EurekaServer检测到超过85%的Client没有正常的心跳,那么Eureka就会认为出现了网络故障,Server将进入保护模式,此时会出现以下情况:
- Eureka Server不再从注册列表中移除因为长时间没收到心跳而应该过期的服务。
- Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用。
- 当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中。
自我保护开关
通过配置
eureka.server.enable-self-preservation
为true打开/false禁用自我保护,开发过程中一般选择禁用。 -
Eureka两种组件下的三种角色
- Eureka Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址
- 提供服务注册:存储微服务注册信息
- 提供服务信息提供:提供注册列表给client获取信息
- 提供服务管理:通过Client的Cancel,Renew等方式维护服务
- 信息同步:每个Server也是一个Client,多个Eureka之间通过P2P复制方式完成服务注册表的同步,同步时被同步信息不会被同步出去,即有3个服务,1同步2时,2不会将被同步信息同步给3,只由1后续同步给3。
- Eureka Client:Java客户端,用于简化与Server交互,管理当前微服务。
- Client会拉取、更新和缓存Eureka Server中的信息。即使所有的Eureka Server节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者。
- Client在微服务启动后,会周期性地向Eureka Server发送心跳(默认周期为30秒)以续约自己的信息。
- Client包含服务提供者Applicaton Service和服务消费者Application Client
- Eureka Provider :启动后向Eureka注册自己信息(地址,提供什么服务)
- Eureka Consumer :向Eureka Server订阅服务,Eureka Server会将对应服务的所有提供者地址列表发送给消费者,并且定期更新。
- 大多服务本身即是提供者,也是消费者。
- Eureka Server:就是服务注册中心(可以是一个集群),对外暴露自己的地址
-
Eureka的工作原理
基本架构
图片来自:https://blog.csdn.net/wys5wys/article/details/103519876
图片来自:https://www.jianshu.com/p/1016cae4fc29
6.实例搭建
父工程创建
<?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>org.example</groupId>
<artifactId>springcloud</artifactId>
<!--表示聚合父工程-->
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>user-service</module>
<module>consumer-demo</module>
<module>eureka-server</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<!--cloud-->
<spring-cloud.version>Greenwich.SR1</spring-cloud.version>
<!--tk mybatis-->
<mapper.starter.version>2.1.5</mapper.starter.version>
<!--mysql-->
<mysql.version>5.1.46</mysql.version>
</properties>
<!--统一管理版本-->
<dependencyManagement>
<dependencies>
<!-- springCloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- 通用Mapper启动器 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>${mapper.starter.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--loombook-->
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Eureka搭建
<?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>springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server</artifactId>
<dependencies>
<!--eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
server:
port: 10086
spring:
application:
name: eureka-server
eureka:
client:
service-url:
# eureka 服务地址,如果是集群,需要指定其他集群地址
defaultZone: HTTP://127.0.0.1:10086/eureka
#不注册自己
register-with-eureka: false
# 不拉取服务
fetch-registry: false
server:
enable-self-preservation: false #关闭自我保护
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author 41209
* 声明当前应用时Eureka服务
*/
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
创建一个服务作为服务提供者
<?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>springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-service</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 通用Mapper启动器 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
server:
port: 9091
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud
username: root
application:
name: user-service
mybatis:
type-aliases-package: com.example.pojo #别名搜索
eureka:
client:
service-url:
defaultZone: HTTP://127.0.0.1:10086/eureka
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import tk.mybatis.spring.annotation.MapperScan;
/**
* @author 41209
*/
@SpringBootApplication
@MapperScan("com.example.mapper")
@EnableDiscoveryClient//开启Euraka服务客户端发现功能
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
pojo
package com.example.pojo;
import lombok.Data;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* @Author: zsh
*/
@Data
@Table(name = "tb_user")
public class User{
// id
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 用户名
private String userName;
// 密码
private String password;
// 姓名
private String name;
// 年龄
private Integer age;
// 性别,1男性,2女性
private Integer sex;
// 出生日期
private Date birthday;
// 创建时间
private Date created;
// 更新时间
private Date updated;
// 备注
private String note;
}
mapper
package com.example.mapper;
import com.example.pojo.User;
import tk.mybatis.mapper.common.Mapper;
/**
* @Author: zsh
*/
public interface UserMapper extends Mapper<User> {
}
service
package com.example.service;
import com.example.mapper.UserMapper;
import com.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @Author: zsh
*/
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public User queryById(Long id){
return userMapper.selectByPrimaryKey(id);
}
}
controller
package com.example.controller;
import com.example.pojo.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
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;
/**
* @Author: zsh
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id){
return userService.queryById(id);
}
}
创建服务消费者
<?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>springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>consumer-demo</artifactId>
<!--提供网络服务-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka客户端 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
</project>
spring:
application:
name: consumer-demo
eureka:
client:
service-url:
defaultZone: HTTP://127.0.0.1:10086/eureka
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author 41209
* Spring提供了一个RestTemplate模板工具类,对基于HTTP的客户端进行了封装,并且实现了对象与json的序列化
* 和反序列化,非常方便。RestTemplate并没有限定HTTP的客户端类型,而是进行了抽象,目前常用的3种都有支
* 持:
* HTTPClient
* OkHTTP
* JDK原生的URLConnection(默认的)
*/
@SpringBootApplication
@EnableDiscoveryClient // 开启Eureka客户端
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
controller
package com.example.controller;
import com.example.user.User;
import lombok.val;
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: zsh
*/
@RestController
@RequestMapping("/consumer")
public class controller {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/{id}")
public User queryById(@PathVariable Long id){
String url = "HTTP://localhost:9091/user/" + id;
List<ServiceInstance> serviceInstances = discoveryClient.getInstances("user-service");
ServiceInstance serviceInstance = serviceInstances.get(0);
url = "http://" + serviceInstance.getHost()+":"+serviceInstance.getPort()+"/user/"+id;
System.out.println(url);
return restTemplate.getForObject(url, User.class);
}
}