Spring Cloud
为何要学习Spring Cloud
单一应用架构 :主要解决ORM 数据库访问层。
垂直应用架构 : 解决分层问题,实现应用的分层开发,提升开发效率。
分布式应用架构:解决系统间调用问题,引发了SOA(面向服务开发)架构新潮。
SOA治理(Macro Service治理):对面向服务开发和治理同时提出新的挑战,要求应用能够做到容易部署、智能路由、服务负载均衡、熔断等要求,能够做到对服务的可视化治理等。
伴随着互联网的发展,人们对这种微服务的开发的呼声越来越大,在互联网的萌芽了两款重量级的SOA治理框架阿里巴巴Dubbo
和SpringFrameWork提供的SpringCloud
由于Spring的广大使用群体也间接的使的Spring Cloud的市场占用率一路飙升。阿里巴巴的Dubbo
也开始发力,由于阿里巴巴的优秀的技术团队和国内较高的知名度,也对Dubbo
框架的发展起到一定的推广的作用,但是相比较于SpringCloud而言,dubbo由于易用性上和对程序员的要求都比Spring Cloud要高一些,因此目前很多互联网公司在做微服务组件开发的时候一般还是使用SpringCloud
居多。
Spring Cloud Ribbon 组件 -负载均衡
Spring Cloud Ribbon 是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。通过SpringCloud的自动配置使得项目可以自动的给RestTemplate添加拦截器。
基于配置文件
- 在项目中集成ribbon
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
....
<!--ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
</dependencies>
- 配置类中添加@LoadBalanced注解
@SpringBootApplication
public class UserApplciation {
public static void main(String[] args) {
SpringApplication.run(UserApplciation.class,args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
ribbon底层会将RestTemplate进行加强,实质上是对RestTemplate添加拦截器,用于修改URL中的服务名。
- application.properties
server.port=8887
USER-SERVER.ribbon.listOfServers=localhost:8889,localhost:8888
USER-SERVER.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
- UserController
@RestController
@RequestMapping(value = "usermanager")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/queryUserById")
public User queryUserById(@RequestParam(value = "id") Integer id){
String url="http://USER-SERVER/formUserManager/queryUserById?id={id}";
return restTemplate.getForObject(url,User.class,id);
}
}
这里的缺点是服务器的地址是配置死的,一旦配置的服务节点上线|下线都有可能影响本应用,我们期望该
listOfServers
可以动态变更,达到服务节点的热部署。
基于注解类
- RibbonConfigure
@Configuration
public class RibbonConfigure {
@Bean
public ServerList<Server> ribbonServerList(){
Server server1 = new Server("localhost", 8888);
Server server2 = new Server("localhost", 8889);
return new StaticServerList<Server>(server1,server2);
}
@Bean
public IRule ribbonRule(){
return new RandomRule();
}
}
这里的缺点是服务器的地址是配置死的,一旦配置的服务节点上线|下线都有可能影响本应用,我们期望该
ribbonServerList
可以动态变更,达到服务节点的热部署。
- UserApplciation
@SpringBootApplication
@RibbonClient(name = "USER-SERVICE",configuration = {RibbonConfigure.class})
public class UserApplciation {
public static void main(String[] args) {
SpringApplication.run(UserApplciation.class,args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
RestTemplate restTemplate = new RestTemplate();
return restTemplate;
}
}
- UserController
@RestController
@RequestMapping(value = "usermanager")
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping(value = "/queryUserById")
public User queryUserById(@RequestParam(value = "id") Integer id){
String url="http://USER-SERVER/formUserManager/queryUserById?id={id}";
return restTemplate.getForObject(url,User.class,id);
}
}
Spring Cloud Eureka (服务注册中心)
Spring Cloud Eureka 是 Spring Cloud Netflix 微服务套件中的一部分, 它基于 Netflix Eureka 做了二次封装, 主要负责完成微服务架构中的服务治理功能。 Spring Cloud 通过为Eureka 增加了 Spring Boot 风格的自动化配置,我们只需通过简单引入依赖和注解配置就能让 Spring Boot构建的微服务应用轻松地与 Eureka 服务治理体系进行整合。
Eureka注册中心集群构建
- pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 启动配置类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
- application-eureka-1.properties
server.port=1111
# 指定当前注册中心的服务名称
spring.application.name=eurekaregistry
## 启用注册中心主动失效,并且每次主动失效检测间隔为5s 默认值60s
eureka.server.eviction-interval-timer-in-ms= 5000
## 设置eureka注册中心的响应更新时间
eureka.server.responseCacheUpdateIntervalMs=3000
eureka.server.responseCacheAutoExpirationInSeconds=60
## 配置注册中心的主机名
eureka.instance.instance-id = eureka-1
eureka.instance.hostname = CentOSA
## 服务刷新时间配置,每隔这个时间会主动心跳一次
eureka.instance.lease-renewal-interval-in-seconds= 5
## 服务提供者被认定为丢失心跳超时,失效多久后被删除
eureka.instance.lease-expiration-duration-in-seconds=15
## 配置定时获取|抓取注册中心的数据时间
eureka.client.registry-fetch-interval-seconds= 5
eureka.client.instance-info-replication-interval-seconds= 5
## 配置集群中其他eureka实例,用于本eureka实例的注册方。
eureka.client.region=beijing
eureka.client.availability-zones.beijing=zone-2,zone-3
eureka.client.service-url.zone-2=http://CentOSB:1111/eureka/
eureka.client.service-url.zone-3=http://CentOSC:1111/eureka/
- application-eureka-2.properties
server.port=1111
# 指定当前注册中心的服务名称
spring.application.name=eurekaregistry
## 启用注册中心主动失效,并且每次主动失效检测间隔为5s 默认值60s
eureka.server.eviction-interval-timer-in-ms= 5000
## 设置eureka注册中心的响应更新时间
eureka.server.responseCacheUpdateIntervalMs=3000
eureka.server.responseCacheAutoExpirationInSeconds=60
## 配置注册中心的主机名
eureka.instance.instance-id = eureka-2
eureka.instance.hostname = CentOSB
## 服务刷新时间配置,每隔这个时间会主动心跳一次
eureka.instance.lease-renewal-interval-in-seconds= 5
## 服务提供者被认定为丢失心跳超时,失效多久后被删除
eureka.instance.lease-expiration-duration-in-seconds=15
## 配置定时获取|抓取注册中心的数据时间
eureka.client.registry-fetch-interval-seconds= 5
eureka.client.instance-info-replication-interval-seconds= 5
## 配置集群中其他eureka实例,用于本eureka实例的注册方。
eureka.client.region=beijing
eureka.client.availability-zones.beijing=zone-1,zone-3
eureka.client.service-url.zone-1=http://CentOSA:1111/eureka/
eureka.client.service-url.zone-3=http://CentOSC:1111/eureka/
- application-eureka-3.properties
server.port=1111
# 指定当前注册中心的服务名称
spring.application.name=eurekaregistry
## 启用注册中心主动失效,并且每次主动失效检测间隔为5s 默认值60s
eureka.server.eviction-interval-timer-in-ms= 5000
## 设置eureka注册中心的响应更新时间
eureka.server.responseCacheUpdateIntervalMs=3000
eureka.server.responseCacheAutoExpirationInSeconds=60
## 配置注册中心的主机名
eureka.instance.instance-id = eureka-3
eureka.instance.hostname = CentOSC
## 服务刷新时间配置,每隔这个时间会主动心跳一次
eureka.instance.lease-renewal-interval-in-seconds= 5
## 服务提供者被认定为丢失心跳超时,失效多久后被删除
eureka.instance.lease-expiration-duration-in-seconds=15
## 配置定时获取|抓取注册中心的数据时间
eureka.client.registry-fetch-interval-seconds= 5
eureka.client.instance-info-replication-interval-seconds= 5
## 配置集群中其他eureka实例,用于本eureka实例的注册方。
eureka.client.region=beijing
eureka.client.availability-zones.beijing=zone-1,zone-2
eureka.client.service-url.zone-1=http://CentOSA:1111/eureka/
eureka.client.service-url.zone-2=http://CentOSB:1111/eureka/
- Maven package指令打包
mvn package
- 上传eurekacloud-1.0-SNAPSHOT.jar到CentOSA、CentOSB、CentOSC
[root@CentOSA ~]# java -jar eurekacloud-1.0-SNAPSHOT.jar --spring.profiles.active=eureka-1
[root@CentOSB ~]# java -jar eurekacloud-1.0-SNAPSHOT.jar --spring.profiles.active=eureka-2
[root@CentOSC ~]# java -jar eurekacloud-1.0-SNAPSHOT.jar --spring.profiles.active=eureka-3
- 访问浏览器查看Eureka是否启动成功
Eureka自我保护机制(面试)
由于Eureka再设计上遵循AP原则,一旦Eureka服务节点检测到异常Renews < Renews threshold
(心跳阈值< 接收心跳)Eureka会进入自我保护机制。一旦进入自我保护机制,Eureka就会失去剔除失效节点功能(失去检测功能)。
Renews threshold
:
eureka.server.renewal-percent-threshold= 0.85
集群: (2 x n) x eureka.server.renewal-percent-threshold = (2 * 3) *0.85 = 5.1 = 取 5
单机: 2 × (n+1) × eureka.server.renewal-percent-threshold = 4 * 0.85 = 3.4 取 3
默认自我保护机制是开启的(推荐选项)
eureka.server.enable-self-preservation=true
如果是单机模式很容易触发自我保护,因为
Renews threshold= 2 (1+1) 0.85 = 3
Renews (last min) =( 60 / eureka.instance.lease-renewal-interval-in-seconds=30)* 1 = 2
所以如果构建的是单机版本的Eureka,建议大家关闭自我保护机制。
服务提供方注册服务
- 发布服务
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 配置服务发布信息
# 发布服务
spring.application.name=USER-SERVICE
# 发布服务实例id
eureka.instance.instance-id=001
eureka.instance.prefer-ip-address=true
eureka.instance.lease-renewal-interval-in-seconds=10
eureka.instance.lease-expiration-duration-in-seconds=20
# 只单纯的注册服务,并不获取服务注册列表信息
eureka.client.register-with-eureka=true
eureka.client.healthcheck.enabled=true
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://CentOSA:1111/eureka/,http://CentOSB:1111/eureka/,http://CentOSC:1111/eureka/
服务引用方引用服务
- pom.xml
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--引入 eureka后,无需再次引入Ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- application.peroperties
# 只是单纯的获取注册中心的注册信息,并不注册自己
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://CentOSA:1111/eureka/,http://CentOSB:1111/eureka/,http://CentOSC:1111/eureka/
Spring Cloud Hystrix (熔断器)
Hystrix是一个延迟和容错库,旨在隔离对远程系统,服务和第三方库的访问点,停止级联故障,并在复杂的分布式系统中实现弹性,在这些系统中,故障是不可避免的。
熔断器集成
- pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<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-hystrix</artifactId>
</dependency>
</dependencies>
- 在需要熔断的方法是添加@HystrixComand注解
@HystrixCommand
@Override
public Integer sum(Integer x, Integer y) {
System.out.println("thread:"+Thread.currentThread().getId());
int b=10/0;
return (x+y)*2;
}
- 在启动类上添加@EnableCircuitBreaker
@SpringBootApplication
@EnableCircuitBreaker
public class HystrixSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixSpringBootApplication.class,args);
}
}
线程隔离
默认该方法的执行会启动新的线程执行和主程序不在一个线程中,因此如果上下文中存在ThreadLocal变量,在该方法中就失效了。因此一般可以通过设置commandProperties注解属性,设置线程就可以了。
@HystrixCommand(commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy",value = "SEMAPHORE")
})
@Override
public User queryUserById(Integer id) {
System.out.println(Thread.currentThread().getId());
return userDAO.queryById(id);
}
execution.isolation.strategy
该属性的可选值有两个THREAD
和SEMAPHORE
默认值是THREAD
.①一般如果一个实例一秒钟有100个并发,此时因为频繁启动线程的开销过大此时一般考虑使用SEMAPHORE,②非网络调用。
Fallback
过在@HystrixCommand中声明fallbackMethod的名称可以实现优雅降级,如下所示:
@HystrixCommand(
fallbackMethod = "failback4Sum",
// ignoreExceptions = {ArithmeticException.class},
commandProperties = {
@HystrixProperty(name="execution.isolation.strategy",value="SEMAPHORE")
}
)
@Override
public Integer sum(Integer x, Integer y) {
System.out.println("thread:"+Thread.currentThread().getId());
int b=10/0;
return (x+y)*2;
}
public Integer failback4Sum(Integer x, Integer y,Throwable e) {
System.out.println(e.getMessage());
return x+y;
}
注意要求fallbackMethod方法和目标方法必须在同一个类中,具有相同的参数(异常参数可选)
请求超时熔断
用户可以通过设置execution.isolation.thread.timeoutInMilliseconds
属性设置一个方法最大请求延迟,系统会抛出HystrixTimeoutException
@HystrixCommand(
fallbackMethod = "failback4Sum",
commandProperties = {
@HystrixProperty(name="execution.isolation.strategy",value="THREAD"),
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "100")
}
)
@Override
public Integer sum(Integer x, Integer y) {
try {
int ms=new Random().nextInt(200);
System.out.println("sleep:"+ms);
Thread.sleep(ms);
} catch (Exception e) {
System.err.println(e.getMessage());
}
return (x+y)*2;
}
public Integer failback4Sum(Integer x, Integer y,Throwable e) {
return x+y;
}
熔断器限流
阅读:https://blog.csdn.net/tongtong_use/article/details/78611225
hystrix dashbord(监测)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
- application.properties
# 开启健康检查的所有访问接口
management.endpoints.web.exposure.include=*
- HystrixSpringBootApplication
@SpringBootApplication
@EnableCircuitBreaker
@EnableHystrixDashboard
public class HystrixSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixSpringBootApplication.class,args);
}
}
可以看到:
- 完整熔断器配置
@Service
public class UserService implements IUserService {
public User failback4QueryUserById(Integer id) {
return new User(-1,"服务降级!");
}
@HystrixCommand(
fallbackMethod = "failback4QueryUserById",
commandProperties = {
@HystrixProperty(name="execution.isolation.strategy",value="THREAD"),
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "1000"),
// 时间窗口 必须是 numBuckets的整数倍数
@HystrixProperty(name="metrics.rollingStats.timeInMilliseconds",value = "10000"),//设置窗口长度10秒
@HystrixProperty(name="metrics.rollingStats.numBuckets",value = "10"),//设置窗口滑动间隔1s
//闸刀开启的条件 请求数> 2 && 错误率> 50%
@HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "20"),//设置最小请求次数
@HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50"),//设置错误率50%
@HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value = "5000"),// 当全开到半开的时间间隔
},
threadPoolProperties = {
@HystrixProperty(name="coreSize",value="10"),//设置线程池大小
@HystrixProperty(name="queueSizeRejectionThreshold",value = "10")//设置队列最大等待请求数
}
)
@Override
public User queryUserById(Integer id) {
int random = new Random().nextInt(3000);
try {
System.out.println("sleep:"+random);
Thread.sleep(random);
} catch (InterruptedException e) {
}
return new User(id,"user"+id);
}
}
Spring Cloud OpenFeign(Rest客户端)
Feign是一个声明性的Web服务客户端。它使编写Web服务客户端变得更容易。Feign 增加了对Spring MVC注解的支持,并使用了Spring MVC中相同HttpMessageConverters。 Feign集成了Ribbon、Eureka、Hystrix,在使用Feign时提供负载均衡以及容错的的http客户端。
快速入门
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</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>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- application.properties
server.port=9999
# 配置Eureka 拦截参数
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.healthcheck.enabled=true
eureka.client.service-url.defaultZone=http://CentOSA:1111/eureka/,http://CentOSB:1111/eureka/,http://CentOSC:1111/eureka/
- UserFeignControllerClient
@FeignClient(name = "USER-SERVICE")
public interface UserFeignControllerClient {
@PostMapping(value = "/formUserManager/addUser")
@ResponseBody
public void addUser(User user) throws IOException;
@GetMapping(value = "/formUserManager/queryUserById")
@ResponseBody
public User queryUserById(@RequestParam(value = "id") Integer id);
@GetMapping(value = "/formUserManager/queryUserByPage")
@ResponseBody
public Map<String, Object> queryUserByPage(@RequestParam(value = "page", defaultValue = "1") Integer pageNow,
@RequestParam(value = "rows", defaultValue = "10") Integer pageSize,
@RequestParam(value = "column", required = false) String column,
@RequestParam(value = "value", required = false) String value);
@DeleteMapping(value = "/formUserManager/delteUserByIds")
@ResponseBody
public void delteUserByIds(@RequestParam(value = "ids") Integer[] ids);
@PutMapping(value = "/formUserManager/updateUser")
@ResponseBody
public void updateUser(User user) throws IOException;
}
- FeignSpringBootApplication
@SpringBootApplication
@EnableFeignClients
public class FeignSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(FeignSpringBootApplication.class,args);
}
}
配置熔断
默认Feign没有开启熔断策略,需要用户在配置文件中指定
- application.properties
server.port=9999
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.healthcheck.enabled=true
eureka.client.service-url.defaultZone=http://CentOSA:1111/eureka/,http://CentOSB:1111/eureka/,http://CentOSC:1111/eureka/
# 开启hystrix
feign.hystrix.enabled=true
# 配置服务访问超时
feign.client.config.USER-SERVICE.connect-timeout=100
feign.client.config.USER-SERVICE.read-timeout=100
- 修改UserFeignControllerClient,添加failback属性
@FeignClient(name = "USER-SERVICE",fallback = UserFeignControllerFailBack.class)
public interface UserFeignControllerClient {
@PostMapping(value = "/formUserManager/addUser")
@ResponseBody
public void addUser(User user) throws IOException;
@GetMapping(value = "/formUserManager/queryUserById")
@ResponseBody
public User queryUserById(@RequestParam(value = "id") Integer id);
@GetMapping(value = "/formUserManager/queryUserByPage")
@ResponseBody
public Map<String, Object> queryUserByPage(@RequestParam(value = "page", defaultValue = "1") Integer pageNow,
@RequestParam(value = "rows", defaultValue = "10") Integer pageSize,
@RequestParam(value = "column", required = false) String column,
@RequestParam(value = "value", required = false) String value);
@DeleteMapping(value = "/formUserManager/delteUserByIds")
@ResponseBody
public void delteUserByIds(@RequestParam(value = "ids") Integer[] ids);
@PutMapping(value = "/formUserManager/updateUser")
@ResponseBody
public void updateUser(User user) throws IOException;
}
- UserFeignControllerFailBack
@Component
public class UserFeignControllerFailBack implements UserFeignControllerClient {
@Override
public void addUser(User user) throws IOException {
}
@Override
public User queryUserById(Integer id) {
User user = new User("故障", false, "***", new Date(), "xxxx");
user.setId(id);
return user;
}
@Override
public Map<String, Object> queryUserByPage(Integer pageNow, Integer pageSize, String column, String value) {
return null;
}
@Override
public void delteUserByIds(Integer[] ids) {
}
@Override
public void updateUser(User user) throws IOException {
}
}
Hystrix Dashboard
略
Spring Cloud Config
Spring Cloud Config 是 Spring Cloud 团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持, 它分为服务端与客户端两个部分。服务端称为分布式配置中心, 它是一个独立的微服务应用, 用来连接配置仓库并为客户端提供获取配置信息、 加密/解密信息等访问接口;客户端微服务架构中的各个微服务应用或基础设施, 它们通过指定的配置中心来管理应用资源与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。
服务端
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
- SpringBootConfigServer
@SpringBootApplication
@EnableConfigServer
public class SpringBootConfigServer {
public static void main(String[] args) {
SpringApplication.run(SpringBootConfigServer.class,args);
}
}
- 服务端配置文件
server.port = 8989
# 连接远程git读取配置信息
#spring.cloud.config.server.git.uri = file:///E:/config
spring.cloud.config.server.git.uri = https://github.com/gaozhy520/config.git
management.endpoints.web.exposure.include=*
必须保证
E:/config
是maven的仓库
- 启动配置服务器
SpringCloud的configServer会启动一个web服务,用于提供配置信息给其他机器。一般提供的访问格式如下
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
其中application
表示应用名字,profile
表示运行的环境参数、label
可以理解为git分支选配,默认值是master
访问地址:http://localhost:8989/SpringCloudConfigServer/test/master
客户端
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
- ConfigClientApplication
@SpringBootApplication
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class,args);
}
}
- application.properties
server.servlet.context-path= /SpringCloudConfigClient
- bootstrap.properties(注意必须将ConfigClient配置写在这个文件中)
# 指定连接的配置服务名字
spring.application.name=app1
spring.cloud.config.profile=test
spring.cloud.config.label=master
spring.cloud.config.name= SpringCloudConfigServer
spring.cloud.config.uri=http://localhost:8989
# 开启所有的健康检查
management.endpoints.web.exposure.include=*
如果不指定
spring.cloud.config.name
系统会使用spring.application.name
去请求参数
注意: 上面这些属性必须配置在 bootstrap.properties 中, 这样配置服务器中的配置信息才能被正确加载。
多仓库配置
server.port = 8989
# 连接远程git读取配置信息
#spring.cloud.config.server.git.uri = file:///E:/config
spring.cloud.config.server.git.uri = https://github.com/gaozhy520/config.git
spring.cloud.config.server.git.repos.aa.search-paths=dir1
spring.cloud.config.server.git.repos.aa.clone-on-start=true
spring.cloud.config.server.git.repos.aa.pattern=app*
spring.cloud.config.server.git.repos.aa.uri= https://github.com/gaozhy520/config.git
spring.cloud.config.server.git.repos.aa.refresh-rate=5
management.endpoints.web.exposure.include=*
客户端刷新RefreshScope
@RestController
@RefreshScope
public class TestController {
@Value("${name}")
private String name;
@GetMapping("showName")
public String showName(){
System.out.println(this.getClass());
return name;
}
}
执行以下shell
配置服务集成Eureka
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- application.properties
server.port = 8989
# 连接远程git读取配置信息
#spring.cloud.config.server.git.uri = file:///E:/config
spring.cloud.config.server.git.uri = https://github.com/gaozhy520/config.git
spring.cloud.config.server.git.repos.aa.search-paths=dir1
spring.cloud.config.server.git.repos.aa.clone-on-start=true
spring.cloud.config.server.git.repos.aa.pattern=app*
spring.cloud.config.server.git.repos.aa.uri= https://github.com/gaozhy520/config.git
spring.cloud.config.server.git.repos.aa.refresh-rate=5
management.endpoints.web.exposure.include=*
spring.application.name=CONFIG-SERVICE
eureka.instance.instance-id=001
eureka.instance.prefer-ip-address=true
eureka.instance.lease-expiration-duration-in-seconds=20
eureka.instance.lease-renewal-interval-in-seconds=10
eureka.client.register-with-eureka=true
eureka.client.healthcheck.enabled=true
eureka.client.fetch-registry=false
eureka.client.service-url.defaultZone=http://CentOSA:1111/eureka/,http://CentOSB:1111/eureka/,http://CentOSC:1111/eureka/
配置客户端集成Eureka
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- bootstrap.properties
# 指定连接的配置服务名字
spring.application.name=aa
spring.cloud.config.profile=test
spring.cloud.config.label=master
spring.cloud.config.name= SpringCloudConfigServer
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=CONFIG-SERVICE
#spring.cloud.config.uri=http://localhost:8989
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.service-url.defaultZone=http://CentOSA:1111/eureka/,http://CentOSB:1111/eureka/,http://CentOSC:1111/eureka/
# 开启所有的健康检查
management.endpoints.web.exposure.include=*
Spring Cloud Bus
在微服务架构的系统中, 我们通常会使用轻量级的消息代理来构建一个共用的消息主题让系统中所有微服务实例都连接上来, 由于该主题中产生的消息会被所有实例监听和消费, 所以我们称它为消息总线。在总线上的各个实例都可以方便地广播一些需要让其他连接在该主题上的实例都知道的消息, 例如配置信息的变更或者其他一些管理操作等。由于消息总线在微服务架构系统中被广泛使用, 所以它同配置中心一样, 几乎是微服务架构中的必备组件。Spring Cloud 作为微服务架构综合性的解决方案,对此自然也有自己的实现, 这就是本章我们将要具体介绍的Spring Cloud Bus。通过使用 Spring Cloud Bus, 可以非常容易地搭建起消息总线, 同时实现了一些消息总线中的常用功能, 比如, 配合Spring Cloud Config 实现微服务应用配置信息的动态更新等。
- pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
- 在配置文件中添加消息总线配置
spring.kafka.bootstrap-servers=CentOSA:9092,CentOSB:9092,CentOSC:9092
- 发布更新服务
注意:所有的节点都可以执行 消息的 总线的发布和订阅信息。其中aa:9090中aa表示刷新的应用9090表示刷新服务的端口。
Spring Cloud Zuul 路由(反向代理)
路由是微服务架构不可或缺的一部分。例如/
可能映射到您的Web应用程序,/api/users
映射到用户服务,/api/shop
映射到商店服务。 Zuul是Netflix的基于JVM的路由器和服务器端负载均衡器。
快速入门
- pom引入zuul依赖
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
- 配置application.properties
server.port=8888
zuul.routes.users.path=/users/**
zuul.routes.users.url=http://localhost:8080
management.endpoints.web.exposure.include=*
启动入口类添加@EnableZuulProxy
注解
@SpringBootApplication
@EnableZuulProxy
public class ZuulSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulSpringBootApplication.class,args);
}
}
用户可以访问http://localhost:8888/users/manager/user/8
系统底层会将用户请求转发给后台http://localhost:8080/manager/user/8
服务。
实现服务负载均衡
- pom.xml文件添加Eureka依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- application.properties
server.port=8888
zuul.routes.users.path=/users/**
zuul.routes.users.service-id=USER-SERVICE
management.endpoints.web.exposure.include=*
# 配置Eureka
eureka.instance.appname=ZUUL-EUREKA
eureka.instance.instance-id=001
eureka.instance.prefer-ip-address=true
eureka.instance.lease-expiration-duration-in-seconds=20
eureka.instance.lease-renewal-interval-in-seconds=10
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.healthcheck.enabled=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
此时,用户可以尝试多启动几个USER-SERVICE的实例,测试zuul的负载均衡效果。
Cookies和Sensitive Headers
默认情况下Zuul会尝试去除HTTP请求头和cookies中一些信息,这样就导致后台系统获取不到某些请求头,用户可以关闭.
server.port=8888
zuul.routes.users.path=/users/**
zuul.routes.users.service-id=USER-SERVICE
zuul.routes.users.sensitive-headers=
management.endpoints.web.exposure.include=*
# 配置Eureka
eureka.instance.appname=ZUUL-EUREKA
eureka.instance.instance-id=001
eureka.instance.prefer-ip-address=true
eureka.instance.lease-expiration-duration-in-seconds=20
eureka.instance.lease-renewal-interval-in-seconds=10
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.healthcheck.enabled=true
eureka.client.service-url.defaultZone=http://jiangzz:123456@localhost:8761/eureka/
集成熔断failBack
Zuul中给定路径的电路跳闸时,您可以通过创建FallbackProvider类型的bean来提供回退响应。
@Component
public class ZuulFallbackProvider implements FallbackProvider {
@Override
public String getRoute() {
return "*";//表示对所有的Service提供failback
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
System.out.println(cause.getClass());
if (cause instanceof HystrixTimeoutException) {
return response(HttpStatus.GATEWAY_TIMEOUT);
} else {
return response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
private ClientHttpResponse response(final HttpStatus status) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return status;
}
@Override
public int getRawStatusCode() throws IOException {
return status.value();
}
@Override
public String getStatusText() throws IOException {
return status.getReasonPhrase();
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
return new ByteArrayInputStream("er ops!!服务器忙!".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
ribbon重试
添加pom依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
application.properties配置服务
server.port=8888
zuul.routes.users.path=/users/**
zuul.routes.users.service-id=USER-SERVICE
zuul.routes.users.strip-prefix=false
hystrix.command.USER-SERVICE.execution.isolation.strategy=THREAD # 设置线程池隔离
hystrix.command.USER-SERVICE.execution.isolation.thread.timeoutInMilliseconds=10000 # 设置hystrix超时
zuul.retryable=true # 开启重试策略
USER-SERVICE.ribbon.ConnectTimeout=100 # 设置连接超时时间
USER-SERVICE.ribbon.ReadTimeout=500 # 设置响应时间
USER-SERVICE.ribbon.MaxAutoRetriesNextServer=1 # 如果连接失败,尝试其他机器的次数
USER-SERVICE.ribbon.MaxAutoRetries=2 # 如果本次失败尝试次数
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# 配置Eureka
eureka.instance.appname=ZUUL-EUREKA
eureka.instance.instance-id=001
eureka.instance.prefer-ip-address=true
eureka.instance.lease-expiration-duration-in-seconds=20
eureka.instance.lease-renewal-interval-in-seconds=10
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.healthcheck.enabled=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
Hystrix限流
application.properties
server.port=8888
zuul.routes.users.path=/users/**
zuul.routes.users.service-id=USER-SERVICE
zuul.routes.users.strip-prefix=false
hystrix.command.USER-SERVICE.execution.isolation.strategy=THREAD
hystrix.command.USER-SERVICE.execution.isolation.thread.timeoutInMilliseconds=10000
hystrix.threadpool.default.coreSize=10 #设置线程池
hystrix.threadpool.default.maxQueueSize=10 #设置最大队列
hystrix.threadpool.default.queueSizeRejectionThreshold=10 #设置最大队列限制
zuul.retryable=true
USER-SERVICE.ribbon.ConnectTimeout=100
USER-SERVICE.ribbon.ReadTimeout=500
USER-SERVICE.ribbon.MaxAutoRetriesNextServer=1
USER-SERVICE.ribbon.MaxAutoRetries=2
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
# 配置Eureka
eureka.instance.appname=ZUUL-EUREKA
eureka.instance.instance-id=001
eureka.instance.prefer-ip-address=true
eureka.instance.lease-expiration-duration-in-seconds=20
eureka.instance.lease-renewal-interval-in-seconds=10
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=true
eureka.client.healthcheck.enabled=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/