※快速入门Spring Cloud必看
一.引导概述
(一)传统项目开发–单点架构
- 传统的开发模式采用单点架构,所有的业务代码都放在同一个war工程中,然后部署到Tomcat中,这样做的好处是开发简单粗暴,没有其他开销,人力成本低。但是不难发现,缺点也是很明显的,团队协作困难,部署不够灵活,稳定性不高,扩展性不够,所有开发人员都围着一个war工程转。
总结起来,适合于短平快小项目。
(二)流行项目开发现状–分布式架构
- 既然把所有业务代码放在一起带来这么多弊端,那理所当然地想到要把这些业务代码分开来,分成不同的项目,部署在不同的服务器当中去。所以什么是分布式架构:分布式架构就是把我们的项目工程按照一定的原则划分成不同的项目,然后部署到不同的服务器中去,它强调的是分,至于怎么分,按照什么的原则,请看下文。
- 优点:业务驱动,轻松扩展,容错机制,管理轻松。
- 缺点:人员成本高,设备成本高,架构设计要求高,调试麻烦。
- 总结:适用于大业务、高并发、高可用场景
(三)分布式架构原则–微服务原则
- 微服务是我们把项目进行拆分的其中一个原则,它强调我们按照我们项目的业务逻辑进行拆分部署。例如一个电商项目,按照业务划分,有订单模块,用户模块,商品模块,购物车模块等。这些模块独立地拥有自己的Controller层,Dao层,Service层,数据库,独立地部署到服务器中。在前端页面中调用这些模块,达到一个清晰明朗的架构要求。
- 微服务原则也叫做SOA(面向服务架构)
(四)学习前提知识
- 最好之前已经接触过Spring boot2.x,不熟练也没关系
- 基本的Web开发能力
(五)个人电脑环境
- 安装好jdk:1.7以上
- 开发工具:IDE(推荐)或者Eclipse
二.必备知识概述
(一)搞分布式的两位大佬及对比
(二)spring cloud介绍
Spring Cloud是一个开发工具集,包含了多个子项目,基于Spring Boot的开发便利,对Netfilx开源组件(美国的宜家做视频的公司)的进一步封装。总结来说,它是做分布式的一套式解决方案。
(三)spring cloud如何搞分布式架构的
先从简单来说,spring cloud主要分为以下几个部分
- 注册中心。负责统一管理所有的项目模块。
- 网关。负责与用户或者说前端交互的接口。(也需要被注册中心管理)
- 配置中心。负责项目中的配置文件的统一管理。(也需要被注册中心管理)
- 具体的业务模块项目。负责具体的业务处理(也需要被注册中心管理)
- 以一个简单的人事分布式项目为例子架构
三.搭建注册中心(Eureka)
Eureka注册中心是spring Cloud中默认的组件,简单易用,但是听说闭源了,所以这里以Eureka入门,往后可以使用Consul后者Zookeep作为替补方案。具体的搭建Eureka注册中心步骤如下。
(一)创建springboot工程,导入依赖
使用IDE的spring initize,勾上Eureka Server即可快速创建工程
(二)在启动类头上加入
## @EnableEurekaServer
(三)修改配置文件
spring.application.name=registry
server.port=9001
eureka.client.register-with-eureka=true
#自动剔除服务
eureka.server.enable-self-preservation=false
eureka.server.eviction-interval-timer-in-ms=60000
#使用ip地址映射
eureka.instance.prefer-ip-address=true
(四)浏览器中输入localhost:9001/即可进入界面管理
(四)Eureka高可用
要达到高可用,除了分布式之外,还需要涉及到的是集群架构,集群就是相同的项目弄多一份。注册中心项目可复制多一份或者修改端口参数
//并修改配置让注册中心互相注册
eureka.client.service-url.defaultZone=http://localhost:9002/eureka
`
四.搭建业务项目到注册中心
(一)创建springboot工程,导入依赖
使用IDE的spring initize,勾上Spring web,Eureka Discover Client即可快速创建工程并加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
(二)在注解类上加入
@EnableDiscoveryClient
(三)修改配置文件
#配置spring cloud客户端
spring.application.name=userService
eureka.client.service-url.defaultZone=http://10.85.2.15:9002/eureka/,http://10.85.2.15:9001/eureka/
server.port=9091
#映射ip地址
eureka.instance.prefer-ip-address=true
#自动检测心跳
eureka.client.healthcheck.enabled = true
eureka.instance.lease-renewal-interval-in-seconds =10
eureka.instance.lease-expiration-duration-in-seconds =30
五.搭建网关统一访问接口Zuul
(一)什么是网关
回想一下,我们分布式有这么多的项目,他们都有自己的ip地址和端口,我们访问这些这些项目的时候没有一个统一的ip和端口,这是不现实的。因此网关可以为用户统一一个访问入口点
(二)搭建项目
spring initize勾选Eureka Discover Client和Zuul,并导入一下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
(三)在注解类上加上
@EnableDiscoveryClient
@EnableZuulProxy
(四)修改配置文件
spring.application.name=gateway
eureka.client.service-url.defaultZone=http://10.85.2.15:9001/eureka/,http://10.85.2.15:9002/eureka/
server.port=8081
#自动检测心跳
eureka.client.healthcheck.enabled = true
eureka.instance.lease-renewal-interval-in-seconds =10
eureka.instance.lease-expiration-duration-in-seconds =30
#使用ip地址映射
eureka.instance.prefer-ip-address=true
#定义web网关
zuul.routes.managerWeb=/managerWeb/**
zuul.routes.businessWeb=/businessWeb/**
zuul.routes.customerWeb=/customerWeb/**
zuul.routes.indexWeb=/indexWeb/**
zuul.routes.searchWeb=/searchWeb/**
zuul.routes.goodsDetailWeb=/goodsDetailWeb/**
zuul.routes.cartWeb=/cartWeb/**
zuul.routes.orderWeb=/orderWeb/**
zuul.routes.seckillWeb=/seckillWeb/**
#定义服务网关
zuul.routes.goodsService=/goodsService/**
zuul.routes.searchService=/searchService/**
zuul.routes.goodsDetailProduceService=/goodsDetailProduceService/**
zuul.routes.cartService=/cartService/**
zuul.routes.orderService=/orderService/**
zuul.routes.payService=/payService/**
zuul.routes.seckillService=/seckillService/**
zuul.routes.advertisementService=/advertisementService/**
zuul.routes.userService=/userService/**
zuul.routes.messageService=/messageService/**
zuul.routes.scheduledTaskService=/scheduledTaskService/**
六.搭建配置中心Config
(一)什么是配置中心
回想我们有这么多项目,每个项目里有这么多配置文件,如果在项目上线的时候,要修改配置文件,必须要重新启动然后修改,当中难免会有点麻烦。所有Spring Cloud提供了统一的配置中心Config解决方案。
(二)前提工作
1.由于配置中心是需要个Git联合工作的,所以需要我们先有一个Git平台,在国内的话推荐使用码云,在上面注册账号和密码。
2.在码云中创建一个仓库,里面放的就是我们的配置文件。注意配置文件要以服务ID.properties/yml和服务ID-prd/dev/等等.properties命名。
(三)创建配置中心服务端
1.spring initize,选中Eureka Discovery Client和Config Server。
2.在启动类上加上
@SpringBootApplication
@EnableDiscoveryClient
@EnableConfigServer //启用配置中心
//http://localhost:9100/[clientid]-[profile].yml|properties|json
3.配置文件中
spring:
application:
name: config
cloud:
config:
server:
git:
uri: https://gitee.com/itpro/sc-config
username: 1096402293@qq.com
password: aabbcc112233
server:
port: 9100
(四)改造原来的服务端项目
1.增加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
2.application.yml删掉,新增bootstrap.yml
spring:
application:
name: member
cloud:
config:
discovery:
enabled: true
service-id: config
profile: dev
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka
七.服务间通信
(一)什么是服务间通信
搭建好分布式的服务之后,服务与服务之间会产生一些调用依赖,会员管理服务中可能会调用订单服务模块,这种调用Spring cloud为我们提供了两种解决方案:RestTemplate和Feign
(二)RestTemplate
方案一:使用内置的RestTemplate(没有负载均衡,不建议)
//1. 最简单的情况是使用SpringCloud内置的RestTemplate对象(不推荐)
// RestTemplate底层进行的Http传输就是使用的Apache HttpClient组件
RestTemplate restTemplate = new RestTemplate();
String json = restTemplate.getForObject("http://localhost:8100/info?bid=" + bid , String.class);
方案二:使用RestTemplate+Ribbon负载均衡
//这是负载均衡客户端 , 这是Ribbon的核心组件
@Resource
private LoadBalancerClient loadBalancerClient;
// 采用Ribbon进行客户端负载均衡(讲解原理,但并不适用这种方式)
RestTemplate restTemplate = new RestTemplate();
//获取服务列表,提供负载均衡
ServiceInstance serviceInstance = loadBalancerClient.choose("book");
String host = serviceInstance.getHost();//获取主机名
Integer port = serviceInstance.getPort(); //获取端口号
String json = restTemplate.getForObject("http://" + host + ":"+ port +"/info?bid=" + bid , String.class);
还有一种简写办法
//在启动类中加入
@Bean //将返回的RestTemplate对象注入到IOC容器中
@LoadBalanced //对RestTemplate进行负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
//在调动类注入
@Resource
private RestTemplate restTemplate;
//在方法中
restTemplate.getForObject("http://book/info?bid=" + bid, String.class);
(三)Feign
1.导入openFeign jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.在注解类上加入
@EnableFeignClients//启用Feign客户端
3.创建一个远程服务接口
@FeignClient(name="book") //book是service-id , @FeignClient(name="book")指明这是Book微服务的调用客户端
//Feign默认就支持Ribbon负载均衡,我们只需要部署多个book微服务后,客户端便可自动进行负载均衡选择
public interface BookClient {
@GetMapping("/info") //当调用getInfo方法的时候,自动向book微服务的/info发起请求
//调用时自动会将?bid=xxx附加到url中
public String getInfo(@RequestParam("bid") Long bid)
/*Get请求对应GetMapping
Get请求使用RequestParam注解发送参数
Post 请求对应PostMapping
@RequestBody
@PostMapping("/create")
public String create(@RequestBody Map rec);*/
}
4.后面就可像调用本地方法一样调用
八.服务间通信的降级
(一)什么是服务降级
设想一下,在远程调用别的模块时,如果别的模块服务没启动,或者说调用的那个方法出错了或者超时了,那么我的模块就会受到影响,所以服务降级就是当调用别人的模块方法出现问题时,本模块必须有一个替代方法来减少这种影响。Spring Cloud为我们提供Hystrix解决方案,下面针对RestTemplate和Feign分别阐述。
(二)Hystrix+RestTemplate
1.导入Hystrix包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.在启动类上加入
@EnableCircuitBreaker //断路器
3.在本模块的方法头上加入注解
//@HystrixCommand(fallbackMethod = "fallback")
4.编写上述方法,要求参数,返回值和原方法一致
private String fallback(Long id ){
return "当前系统正忙,请稍后再试";
}
5.如果每个方法都这样子写的很麻烦,可以使用全局降级方法
//在类上加上
@DefaultProperties(defaultFallback = "defaultFallBack")
//全局默认的降级方法,不需要参数,且返回String或者任何可以被JSON序列化的对象
//在默认情况下,Hystrix默认的timeout时间为1s
private String defaultFallBack(){
return "[默认系统降级]当前系统正忙,请稍后再试";
}
//在需要降级的那些方法上加上@HystrixCommand
6.如何判定远程调用的方法为调用失败了,可通过修改配置文件
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
(三)Hystrix+Feign
1.导入open-feign jar包,前面已有
2.修改配置文件夹,开启 Hystrix
feign:
hystrix:
enabled: true
3.改写在远程调用的接口
@FeignClient(name="member" , fallback = MemberClient.MemberClientFallBack.class)
public interface MemberClient {
@GetMapping("/check")
public MemberResult checkMobile(@RequestParam("mobile") String mobile);
@Component
static class MemberClientFallBack implements MemberClient{
@Override
public MemberResult checkMobile(String mobile) {
MemberResult mr = new MemberResult();
mr.setCode("0");
mr.setMessage("success");
MemberDTO memberDTO = new MemberDTO();
memberDTO.setMid(0l);
mr.setData(memberDTO);
return mr;
}
}
}
(四)断路机制
如果我们在远程调用其他模块的方法时频繁出错,那么Hystrix就会启动保护机制,下次访问的时候,不管三七二十一直接跳到降级方法,那么如何设定单位时间内出错的次数为断路呢
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
circuitBreaker:
requestVolumeThreshold : 10
sleepWindowInMilliseconds: 10000
errorThresholdPercentage: 60