springcloud系列学习笔记目录参见博主专栏 spring boot 2.X/spring cloud Greenwich。
由于是一系列文章,所以后面的文章可能会使用到前面文章的项目。文章所有代码都已上传GitHub:https://github.com/liubenlong/springcloudGreenwichDemo
本系列环境:Java11;springboot 2.1.1.RELEASE;springcloud Greenwich.RELEASE;MySQL 8.0.5;
Spring提供了一系列工具,可以帮助开发人员迅速搭建分布式系统中的公共组件(比如:配置管理,服务发现,断路器,智能路由,微代理,控制总线,一次性令牌,全局锁,主节点选举, 分布式session, 集群状态)。协调分布式环境中各个系统,为各类服务提供模板性配置。使用Spring Cloud, 开发人员可以搭建实现了这些样板的应用,并且在任何分布式环境下都能工作得非常好,小到笔记本电脑, 大到数据中心和云平台。
本系列文章目的是快速学习 springcloud。详细原理后续介绍。
相关参考链接:
Intellij IDEA
java: 11
springboot :** 2.1.1.RELEASE**
springcloud : Greenwich.RELEASE
关于spring cloud和spring boot的版本使用情况请参考 spring cloud Greenwich 学习笔记(0)spring cloud 与 spring boot的版本对应情况
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能。
Eureka包含两个组件:Eureka Server和Eureka Client。
Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也就是一个内置的、使用轮询(round-robin)负载算法的负载均衡器。
在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
Eureka Server之间通过复制的方式完成数据的同步,Eureka还提供了客户端缓存机制,即使所有的Eureka Server都挂掉,客户端依然可以利用缓存中的信息消费其他服务的API。综上,Eureka通过心跳检查、客户端缓存等机制,确保了系统的高可用性、灵活性和可伸缩性。
eureka现已闭源,停止更新,处于维护阶段。不过目前已经很稳定,依旧可以使用。也可以使用 Consul、zookeeper等替换
本文整体项目结构如下:
最外层pom文件引入springcloud和springboot的依赖
<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.0modelVersion>
<groupId>com.lblgroupId>
<artifactId>springclouddemoartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>pompackaging>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<spring.boot.version>2.1.1.RELEASEspring.boot.version>
<spring.cloud.version>Greenwich.RELEASEspring.cloud.version>
<maven.compiler.source>11maven.compiler.source>
<maven.compiler.target>11maven.compiler.target>
properties>
<modules>
<module>springcloud-eureka-servermodule>
<module>springcloud-eureka-serviceprovidermodule>
<module>springcloud-eureka-serviceconsumer-ribbonmodule>
modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring.boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring.cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
project>
首先编写eureka注册中心,也就是服务端。
该注册中心继承于上面方父module:springclouddemo
<parent>
<groupId>com.lblgroupId>
<artifactId>springclouddemoartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>javax.xml.bindgroupId>
<artifactId>jaxb-apiartifactId>
dependency>
<dependency>
<groupId>com.sun.xml.bindgroupId>
<artifactId>jaxb-implartifactId>
<version>2.3.0version>
dependency>
<dependency>
<groupId>org.glassfish.jaxbgroupId>
<artifactId>jaxb-runtimeartifactId>
<version>2.3.0version>
dependency>
<dependency>
<groupId>javax.activationgroupId>
<artifactId>activationartifactId>
<version>1.1.1version>
dependency>
dependencies>
说明:这里我们额外引入了jaxb
。是因为 运行springboot项目出现:Type javax.xml.bind.JAXBContext not present
配置文件:
server:
port: 8080
# 最佳实践:springcloud应用都要指定application.name
spring:
application:
name: springcloud-eureka-server
# 在默认情况下erureka server也是一个eureka client ,必须要指定一个 server
eureka:
instance:
hostname: localhost
client:
# registerWithEureka和fetchRegistry=false 表明自己是一个eureka server.
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
这里我们制定了端口、应用名称、eureka配置。各项配置含义参见注释。
编写启动类,这里需要引入@EnableEurekaServer
注解来启用EurekaServer:
@SpringBootApplication
@EnableEurekaServer //启用EurekaServer
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
启动服务以后,浏览器访问http://127.0.0.1:8080/
就是eureka的控制台了,这里可以看到注册的所有应用:
注册中心搭建好了,那么现在编写服务提供者项目springcloud-eureka-serviceprovider
。
这里需要引入依赖eureka-client
【不管是服务提供者还是消费者,在eureka眼里都是客户端】:
<artifactId>springcloud-eureka-serviceproviderartifactId>
<packaging>jarpackaging>
<name>springcloud-eureka-serviceprovidername>
<parent>
<groupId>com.lblgroupId>
<artifactId>springclouddemoartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>javax.xml.bindgroupId>
<artifactId>jaxb-apiartifactId>
dependency>
<dependency>
<groupId>com.sun.xml.bindgroupId>
<artifactId>jaxb-implartifactId>
<version>2.3.0version>
dependency>
<dependency>
<groupId>org.glassfish.jaxbgroupId>
<artifactId>jaxb-runtimeartifactId>
<version>2.3.0version>
dependency>
<dependency>
<groupId>javax.activationgroupId>
<artifactId>activationartifactId>
<version>1.1.1version>
dependency>
dependencies>
配置文件中需要指定应用名称以及配置中心地址:
server:
port: 8082
# 服务与服务之间相互调用一般都是根据这个name 。
spring:
application:
name: springcloud-eureka-serviceprovider
eureka:
client:
serviceUrl:
# 指定服务注册中心的地址
defaultZone: http://localhost:8080/eureka/
编写启动类,顺便提供一个restAPI服务
@SpringBootApplication
@EnableEurekaClient //启用EurekaClient服务
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Value("${server.port}")
String port;
@RequestMapping("/")
public String hi(String name) {
return String.format("hello %s , from port=%s", name, port);
}
}
这里我们启动两个springcloud-eureka-serviceprovider
服务来模拟集群。将配置文件中的端口改为8083,然后将IDEA中单实例的配置去掉:
左上角的single instance only
去掉,就可以启动多个项目了。启动端口8083的springcloud-eureka-serviceprovider
。
在eureka控制台查看服务信息,可以发现注册了两个服务8082和8083:
来看一下restcontroller还不好使:
至此注册中心搭建完成,服务提供者也对外提供服务了。接下来该搭建服务消费者了。
springcloud提倡微服务采用rest http的方式。消费者在注册中心中发现服务后,需要通过负载均衡进行调度,springcloud全家桶提供了两种服务调用方式,一种是ribbon+restTemplate
,另一种是feign
。其中feign
底层使用了ribbon
。本文介绍ribbon
。
首先引入相关依赖
<artifactId>springcloud-eureka-serviceconsumer-ribbonartifactId>
<packaging>jarpackaging>
<name>springcloud-eureka-serviceconsumer-ribbonname>
<parent>
<groupId>com.lblgroupId>
<artifactId>springclouddemoartifactId>
<version>1.0-SNAPSHOTversion>
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-ribbonartifactId>
dependency>
<dependency>
<groupId>javax.xml.bindgroupId>
<artifactId>jaxb-apiartifactId>
dependency>
<dependency>
<groupId>com.sun.xml.bindgroupId>
<artifactId>jaxb-implartifactId>
<version>2.3.0version>
dependency>
<dependency>
<groupId>org.glassfish.jaxbgroupId>
<artifactId>jaxb-runtimeartifactId>
<version>2.3.0version>
dependency>
<dependency>
<groupId>javax.activationgroupId>
<artifactId>activationartifactId>
<version>1.1.1version>
dependency>
dependencies>
配置文件同样需要指定应用名称及注册中心地址
server:
port: 8085
# 服务与服务之间相互调用一般都是根据这个name 。
spring:
application:
name: springcloud-eureka-serviceconsumer-ribbon
eureka:
client:
serviceUrl:
# 指定服务注册中心的地址
defaultZone: http://localhost:8080/eureka/
编写启动类
@SpringBootApplication
@EnableEurekaClient //启用EurekaClient组件
//@EnableDiscoveryClient //启用服务发现组件
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
/**
* springcloud中提倡rest风格的微服务
* 想spring容器中注入RestTemplate
*
* 使用LoadBalanced表明这个restRemplate开启负载均衡的功能。
* @return
*/
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
启动类中通过注解启用了Eureka和服务发现组件,并且向spring容器中注入了开启了负载均衡功能的RestTemplate。
编写controller
@RestController
public class HelloController {
@Autowired
RestTemplate restTemplate;
@RequestMapping("/")
public String hi(String name){
//这里直接写的是服务名: springcloud-eureka-serviceprovider 。在ribbon中它会根据服务名来选择具体的服务实例,根据服务实例在请求的时候会用具体的url替换掉服务名
return restTemplate.getForObject("http://springcloud-eureka-serviceprovider?name=" + name, String.class);
}
}
在controller中我们使用restTemplate来调用服务提供者,注意这里我们没有写IP和端口。而是通过服务名称进行调用的。
启动服务,浏览器多次访问http://127.0.0.1:8085/?name=a
,会发现下面两个result轮询出现
这是因为我们服务提供者启动了两个,端口分别是8082和8083,通过ribbon进行了客户端负载均衡。
这时我们再看一下注册中心,里面展示了两个服务提供者,一个消费者:
整体的系统架构如下:
官方对于自我保护机制的定义:
https://github.com/Netflix/eureka/wiki/Understanding-Eureka-Peer-to-Peer-Communication
自我保护模式正是一种针对网络异常波动的安全保护措施,使用自我保护模式能使Eureka集群更加的健壮、稳定的运行。
自我保护机制的工作机制是如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制,此时会出现以下几种情况:
因此Eureka Server可以很好的应对因网络故障导致部分节点失联的情况,而不会像ZK那样如果有一半不可用的情况会导致整个集群不可用而变成瘫痪。
这个只适用于测试环境,在生产环境不要关闭
** 服务端配置**
eureka:
server:
# 测试时关闭自我保护机制,保证不可用服务及时踢出
enable-self-preservation: false
** 客户端配置**
# 心跳检测检测与续约时间
# 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服务
eureka:
instance:
lease-renewal-interval-in-seconds: 1 # 每间隔1s,向服务端发送一次心跳,证明自己依然”存活“
lease-expiration-duration-in-seconds: 2 # 告诉服务端,如果我2s之内没有给你发心跳,就代表我“死”了,将我踢出掉
可以看到,上面我将@EnableDiscoveryClient
注释掉了。
其实这两个注解非常的类似,都是用于启用服务发现的。
在stackoverflow
https://stackoverflow.com/questions/31976236/whats-the-difference-between-enableeurekaclient-and-enablediscoveryclient 中介绍了:
There are multiple implementations of “Discovery Service” (eureka, consul, zookeeper). @EnableDiscoveryClient lives in spring-cloud-commons and picks the implementation on the classpath. @EnableEurekaClient lives in spring-cloud-netflix and only works for eureka. If eureka is on your classpath, they are effectively the same.
也就是说discovery service有许多种实现(eureka、consul、zookeeper等等),@EnableDiscoveryClient
在spring-cloud-commons
包中, 而@EnableEurekaClient
是在spring-cloud-netflix
包中的。
如果选用的注册中心是eureka,那么就推荐@EnableEurekaClient,如果是其他的注册中心,那么使用@EnableDiscoveryClient。
springcloud系列学习笔记目录参见博主专栏 spring boot 2.X/spring cloud Greenwich。
由于是一系列文章,所以后面的文章可能会使用到前面文章的项目。文章所有代码都已上传GitHub:https://github.com/liubenlong/springcloudGreenwichDemo
本系列环境:Java11;springboot 2.1.1.RELEASE;springcloud Greenwich.RELEASE;MySQL 8.0.5;