一、微服务基本搭建
1、创建父工程
<?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>
<version>1.0-SNAPSHOT</version>
<modules>
<module>springcloud-api</module>
<module>springcloud-api-provider-8001</module>
<module>springcloud-api-consumer-80</module>
<module>springcloud-eureka-7001</module>
<module>springcloud-eureka-7002</module>
<module>springcloud-eureka-7003</module>
<module>springcloud-api-provider-8002</module>
<module>springcloud-api-provider-8003</module>
<module>springcloud-api-consumer-feign</module>
<module>springcloud-api-provider-hystrix-8001</module>
<module>springcloud-api-consumer-hystrix-dashboard</module>
<module>springcloud-zuul-9527</module>
<module>springcloud-config-server-3344</module>
</modules>
<!--打包方式 pom-->
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<junit.version>4.12</junit.version>
<lombok.version>1.18.20</lombok.version>
<log4j.version>1.2.17</log4j.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<!--SpringCloud的依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SpringBoot的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!--数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.1</version>
</dependency>
<!--SpringBoot启动器-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--=========日志相关=============-->
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!--logback-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2、创建公共实体
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Dept implements Serializable {
private Long deptNo;
private String deptName;
private String dbName;
}
在这里插入代码片
3、服务提供者创建
- 编写pom,xml
- 配置application.yml
- 根据配置新建mybatis-config.xml文件
- 编写部门dao以及对应的mapper
- 编写service接口以及实现类
- 编写controller类提供Rest服务
//提供Restful风格服务
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("dept/add")
public boolean addDept(Dept dept){
return deptService.addDept(dept);
}
@GetMapping("dept/query/{id}")
public Dept queryById(@PathVariable("id")Long id){
Dept dept = deptService.queryById(id);
if (dept==null){
throw new RuntimeException("失败了呀!!!");
}
return dept;
}
@GetMapping("dept/query/all")
public List<Dept> queryAll(){
return deptService.queryAll();
}
}
@SpringBootApplication //启动类
@EnableEurekaClient //服务启动后主动注册到eureka中心
@EnableDiscoveryClient //服务发现
public class DeptProviderStarter_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderStarter_8001.class,args);
}
//增加一个servlet
@Bean
public ServletRegistrationBean bean(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
4、使用Ribbon创建服务消费者
@RestController
public class DeptConsumerController {
@Autowired
//提供多种便捷访问远程http服务的方法,简单的Restful风格
private RestTemplate restTemplate;
// public static final String REST_URL_PREFIX="http://localhost:8001/";
//ribbon负载均衡下使用服务名调用
private static final String REST_URL_PREFIX="http://SPRINGCLOUD-PROVIDER-DEPT/";
@RequestMapping("consumer/dept/query/{id}")
public Dept query(@PathVariable("id")Long id){
return restTemplate.getForObject(REST_URL_PREFIX+"dept/query/"+id,Dept.class);
}
@RequestMapping("consumer/dept/query/all")
public List<Dept> queryAll(){
return restTemplate.getForObject(REST_URL_PREFIX+"dept/query/all",List.class);
}
@RequestMapping("consumer/dept/add")
public Boolean add(Dept dept){
return restTemplate.postForObject(REST_URL_PREFIX+"dept/add",dept,Boolean.class);
}
}
二、Eureka服务注册与发现
1、什么是Eureka
- Eureka是Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。
- Netflix 在设计Eureka 时,遵循CPA原则中的AP原则。
- CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
①、一致性(Consistency)
②、可用性(Availability)
③、分区容错性(Partition tolerance)
2、原理分析
- SpringCloud 封装了NetFlix公司开发的Eureka模块来实现服务注册和发现
- Eureka采用了C-S的架构设计,EurekaServer 作为服务注册功能的服务器,他是服务注册中心
- 而系统中的其他微服务。使用Eureka的客户端连接到EurekaServer并维持心跳连接。这样系统的维护人员就可以通过EurekaServer来监控系统中各个微服务是否正常运行,SpringCloud的一些其他模块(比如Zuul)就可以通过EurekaServer来发现系统中的其他微服务,并执行相关的逻辑;

3、Eureka包含的两个组件
- Eureka Server 提供服务注册服务,各个节点启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
- Eureka Client是一个Java客户端,用于简化EurekaServer的交互,客户端同时也具备一个内置的,使用轮询负载算法的负载均衡器。在应用启动后,将会向EurekaServer发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除掉(默认周期为90秒)
4、Eureka服务端构建
parent>
<artifactId>springcloud</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<!--导包-->
<dependencies>
<!--erueka-server服务端-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
server:
port: 7001
# Eureka配置
eureka:
instance:
hostname: eureka7001.com # eureka服务端的名称
client:
register-with-eureka: false # 表示是否向eureka注册中心注册自己
fetch-register: false # false:表示自己为注册中心
service-url: #监控页面
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ # 集群配置
@EnableEurekaServer //服务端启动类
@SpringBootApplication
public class EurekaServerStarter_7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaServerStarter_7001.class,args);
}
}
5、Eureka客户端构建
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- eureka服务者-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 添加监控信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
server:
port: 8001
# mybatis配置
mybatis:
type-aliases-package: com.lc.pojo
mapper-locations: classpath:mybatis/mapper/*.xml
# spring配置
spring:
application:
name: springcloud-provider-dept # 三个服务名称一致是
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/springcloud?useSSL=true&useUnicode=true&characterEncoding=utf-8
username: root
password: admin123
# eureka注册配置,服务注册到那里
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider-dept-8001 #修改eureka上项目的描述信息
prefer-ip-address: true
# info配置
info:
app.name: liaochang-springcloud
company.name: blog.liaochang.com
@SpringBootApplication //启动类
@EnableEurekaClient //服务启动后主动注册到eureka中心
@EnableDiscoveryClient //服务发现
public class DeptProviderStarter_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderStarter_8001.class,args);
}
//增加一个servlet
@Bean
public ServletRegistrationBean bean(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");
return registrationBean;
}
}
6、Eureka的自我保护机制
- 默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与Eureka之间无法正常通行,以上行为可能变得非常危险了,因为微服务本身其实是健康的,此时本不应该注销这个服务。Eureka通过 自我保护机制 来解决这个问题,当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该EurekaServer节点会自动退出自我保护模式。
- 在自我保护模式中,EurekaServer会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该EurekaServer节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话:好死不如赖活着。
- 综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮和稳定。在SpringCloud中,可以使用 eureka.server.enable-self-preservation = false 禁用自我保护模式 【不推荐关闭自我保护机制】。
- 一句话总结:某时刻某一个微服务不可以用了,eureka不会立刻清理,依旧会对该微服务的信息进行保存。
三、Eureka与Zookeeper的区别
1、CAP理论
- C(Consistency)一致性
- A(Availability)可用性
- P(Partition tolerance)分区容错性
- CA:单点集群,满足一致性,可用性的系统,通常可扩展性较差。
- CP:满足一致性,分区容错性的系统,通常性能不是特别高。
- AP:满足可用性,分区容错性的系统,通常可能对一致性要求低一些。
2、Eureka比Zookeeper好在那里
- 著名的CAP理论指出,一个分布式系统不可能同时满足C(一致性)、A(可用性)、P(容错性)。
- 由于分区容错性P在分布式系统中是必须要保证的,因此我们只能在A和C之间进行权衡。
3、Zookeeper保证的是CP
- 当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。问题在于,选举leader的时间太长,30~120s,且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。在云部署的环境下,因为网络问题使得zk集群失去master节点是较大概率会发生的事件,虽然服务最终能够恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。
4、Eureka保证的是AP
- Eureka看明白了这一点,因此在设计时就优先保证可用性。Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时,如果发现连接失败,则会自动切换至其他节点,只要有一台Eureka还在,就能保住注册服务的可用性,只不过查到的信息可能不是最新的,除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:
①、Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
②、 Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上(即保证当前节点依然可用)
③、 当网络稳定时,当前实例新的注册信息会被同步到其他节点中