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://<span class="katex math inline">{spring.security.user.name}:</span>{spring.security.user.password}@<span class="katex math inline">{eureka.instance.hostname}:</span>{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 服务提供端 {#toc_1}
> 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)查看服务注册中心是否有启动的注册的服务
三、构建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><span class="katex math inline">{spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<!--<finalName></span>{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…
[ 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更新频率将打破服务器的自我保护功能


💬 评论