Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring Cloud并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
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 cloud 是微服务架构的集大成者,将一系列优秀的组件进行了整合。
本章Spring cloud 使用,一步一步带你构建Spring cloud整个应用
- 构建Spring cloud服务端
- 构建Spring cloud 服务提供端
- 构建Spring cloud 消费端(服务调用端)
一、Spring Cloud 服务端
POM.xml 配置
<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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.test.cloud</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <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-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.properties配置
server.port=8761 eureka.instance.hostname=127.0.0.1 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false eureka.client.service-url.defaultZone=http://{spring.security.user.name}:{spring.security.user.password}@{eureka.instance.hostname}:{server.port}/eureka/ # suppress inspection "SpringBootApplicationProperties" spring.security.basic.enabled=true spring.security.user.name=user spring.security.user.password=123456
项目入口Application配置
- 添加 @EnableEurekaServer 注解 在Application中
如果遇到csrf问题的时候,需要实现WebSecurityConfigurerAdapter的configure 方法
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); super.configure(http); } }
直接运行项目 出现下面表示项目启动成功
[ Thread-46] o.s.c.n.e.server.EurekaServerBootstrap : Initialized server context
[ Thread-46] c.n.e.r.PeerAwareInstanceRegistryImpl : Got 1 instances from neighboring DS node
[ Thread-46] c.n.e.r.PeerAwareInstanceRegistryImpl : Renew threshold is: 1
[ Thread-46] c.n.e.r.PeerAwareInstanceRegistryImpl : Changing status to UP
[ Thread-46] e.s.EurekaServerInitializerConfiguration : Started Eureka Server
[ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8761 (http) with context path ''
[ main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8761
[ main] com.test.cloud.demo.DemoApplication : Started DemoApplication in 18.421 seconds (JVM running for 26.067)
[on(2)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
[on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
[on(2)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 16 ms在浏览器输入 127.0.0.1:8761 出现 输入我们在application.properties中配置的用户名和密码, 出现下面界面,表示服务注册中心启动成功,等待服务注册。
二、构建Spring cloud 服务提供端
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"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>clientdemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>clientdemo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.properties配置,注意名称不能包含下划线(—)
server.port=8762 eureka.client.service-url.defaultZone=http://user:123456@127.0.0.1:8761/eureka/ spring.application.name=client-test-demo spring.security.user.name=user spring.security.user.password=123456
入口Application配置
服务提供者需要在Application添加@EnableEurekaClient 注解
添加一个测试的Controller
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("index") public class IndexController { @GetMapping("test") @ResponseBody public String test(){ return "测试-服务提供者"; } }
启动服务提供者,出现如下表示启动成功,可以通过接口调用下上面写的Controller的测试方法是否成功。
scoveryClient : Starting heartbeat executor: renew interval is: 30
[ main] c.n.discovery.InstanceInfoReplicator : InstanceInfoReplicator onDemand update allowed rate per min is 4
[ main] com.netflix.discovery.DiscoveryClient : Discovery Client initialized at timestamp 1566883730293 with initial instances count: 0
[ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application CLIENT-TEST-DEMO with eureka with status UP
[ main] com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [timestamp=1566883730299, current=UP, previous=STARTING]
[nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_CLIENT-TEST-DEMO/192.168.1.78:client-test-demo:8762: registering service…
[ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8762 (http) with context path ''
[ main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8762
[ main] c.e.clientdemo.ClientdemoApplication : Started ClientdemoApplication in 14.831 seconds (JVM running for 21.023)
[nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_CLIENT-TEST-DEMO/192.168.1.78:client-test-demo:8762 – registration status: 204查看服务注册中心是否有启动的注册的服务
三、构建Spring cloud 消费端(服务调用端)
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"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>springxiaofeidemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springxiaofeidemo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR2</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 添加Actuator监控 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>{spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <!--<finalName>{project.artifactId}</finalName>--> <!--<resources>--> <!--<resource>--> <!--<directory>src/main/resources</directory>--> <!--<filtering>true</filtering>--> <!--</resource>--> <!--</resources>--> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <!--<plugin>--> <!--<groupId>org.apache.maven.plugins</groupId>--> <!--<artifactId>maven-resources-plugin</artifactId>--> <!--<configuration>--> <!--<delimiters>--> <!--<delimit>$</delimit>--> <!--</delimiters>--> <!--</configuration>--> <!--</plugin>--> </plugins> </build> </project>
application.properties配置
server.port=8763 eureka.client.service-url.defaultZone=http://user:123456@127.0.0.1:8761/eureka/ spring.application.name=client-request-demo spring.security.user.name=user spring.security.user.password=123456 feign.hystrix.enabled=true
入口Application中添加@EnableEurekaClient和@EnableFeignClients 注解 FeignClients是用来服务间调用的。
@EnableEurekaClient @SpringBootApplication @EnableFeignClients public class SpringxiaofeidemoApplication { public static void main(String[] args) { SpringApplication.run(SpringxiaofeidemoApplication.class, args); } @Bean @LoadBalanced RestTemplate restTemplate(){ return new RestTemplate(); } }
添加测试Controller类
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/index") public class IndexController { @Autowired HelloService helloService; @Autowired RestTemplate restTemplate; @GetMapping("test") public String indexTest(){// 通过HelloService中的注解绑定调用 参考 Helloservice return this.helloService.hello(); } @GetMapping("test1") public String ribbonTest(){ //注解@HystrixCommand 不能加在控制器层,而应该加在Service 层 使用Hystrix进行容错和服务降级 return restTemplate.getForObject("http://CLIENT-TEST-DEMO/index/test",String.class); } }
HelloService接口 import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(value = "CLIENT-TEST-DEMO",fallback = HelloServiceFallback.class) public interface HelloService { @GetMapping(value = "index/test") String hello(); }
HelloServiceFallback 类
import org.springframework.stereotype.Component; @Component public class HelloServiceFallback implements HelloService{ @Override public String hello() { return "测试-服务降权容错提供者"; } }
启动服务
[ main] com.netflix.discovery.DiscoveryClient : Getting all instance registry info from the eureka server
[ main] com.netflix.discovery.DiscoveryClient : The response status is 200
[ main] com.netflix.discovery.DiscoveryClient : Starting heartbeat executor: renew interval is: 30
[ main] c.n.discovery.InstanceInfoReplicator : InstanceInfoReplicator onDemand update allowed rate per min is 4
[ main] com.netflix.discovery.DiscoveryClient : Discovery Client initialized at timestamp 1566884583081 with initial instances count: 1
[ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application CLIENT-REQUEST-DEMO with eureka with status UP
[ main] com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [timestamp=1566884583086, current=UP, previous=STARTING]
[nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_CLIENT-REQUEST-DEMO/192.168.1.78:client-request-demo:8763: registering service…
[nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_CLIENT-REQUEST-DEMO/192.168.1.78:client-request-demo:8763 – registration status: 204
[ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8763 (http) with context path ''
[ main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8763
[ main] c.e.s.SpringxiaofeidemoApplication : Started SpringxiaofeidemoApplication in 17.69 seconds (JVM running for 23.897)
[on(6)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
[on(6)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
[on(6)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 15 ms在浏览器中输入
http://127.0.0.1:8763/index/test结果:测试-服务提供者
http://127.0.0.1:8763/index/test1
结果:测试-服务提供者
如果我们关闭服务提供者会是什么情况呢?
关闭服务提供者
输入:http://127.0.0.1:8763/index/test
结果:测试-服务降权容错提供者
输入:http://127.0.0.1:8763/index/test1
结果:Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Tue Aug 27 13:48:14 CST 2019
There was an unexpected error (type=Internal Server Error, status=500).
No instances available for CLIENT-TEST-DEMO
项目控制台输出java.lang.IllegalStateException: No instances available for CLIENT-TEST-DEMO
如果服务都启动之后关闭注册中心请大家自己试试?
四、其他
上面的3个工程打包为zip放到百度云盘
链接: https://pan.baidu.com/s/1IOWaF_uUzL65_6GQWf6pDA 提取码: kuw8
背景介绍:eureka默认开启了自我保护机制,导致实际上已经停止服务的实例无法从注册中心剔除!
解决方案:
在注册中心(eureka-server端,而不是eureka-client端)添加如下配置:
以下配置仅在开发环境中使用
关闭注册中心的自我保护机制,防止已关闭的实例无法从注册中心剔除
eureka.server.enable-self-preservation=false
背景:由于Eureka拥有自我保护机制,当其注册表里服务因为网络或其他原因出现故障而关停时,Eureka不会剔除服务注册,而是等待其修复。这是AP的一种实现。
为了让其有精准的 CP健康检查,可以采取让其剔除不健康节点。server端:
eureka.server.enable-self-preservation//(设为false,关闭自我保护主要)
eureka.server.eviction-interval-timer-in-ms//清理间隔(单位毫秒,默认是60*1000)
client端:
eureka.client.healthcheck.enabled = true//开启健康检查(需要spring-boot-starter-actuator依赖)
eureka.instance.lease-renewal-interval-in-seconds =10//租期更新时间间隔(默认30秒)
eureka.instance.lease-expiration-duration-in-seconds =30//租期到期时间(默认90秒)实例:
server端配置:
eureka:
server:
enableSelfPreservation: false
evictionIntervalTimerInMs: 4000
client配置:
eureka:
instance:
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 30lease-renewal-interval-in-seconds 每间隔10s,向服务端发送一次心跳,证明自己依然”存活“
lease-expiration-duration-in-seconds 告诉服务端,如果我30s之内没有给你发心跳,就代表我“死”了,将我踢出掉。
注意:更改Eureka更新频率将打破服务器的自我保护功能