SpringCloud
微服务架构
每个工程都是独立的模块,工程之间使用更轻量的http
通讯框架 (不建立依赖关系) 每个微服务都有自己的数据库,每个微服务都是完成模块的具体的功能,都是独立的,只需要对外提供一个接口
服务调用方式
RPC
- 基于Socket
- 自定义数据格式
- 速度快,效率搞
- 典型代表:Dubbo ElasticSearch集群间相互调用
Http
- 基于TCP/IP
- 规定数据传输格式
- 缺点是消息封装比较臃肿,传输速度比较慢
- 优点是对服务提供和调用没有任何技术限制,自由灵活,更符合微服务理念
ResTemplate
- estTemplate是Rest的HTTP客户端模板工具类
- 对基于Http的客户端进行封装
- 实现对象与JSON的序列化与反序列化
- 不限定客户端类型,目前常用的3种客户端都支持:HttpClient、OKHttp、JDK原生URLConnection(默认方式)
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringCloudDemo2RestemplateApplicationTest {
@Autowired
private RestTemplate restTemplate;
@Test
public void testRestTemplate(){
String url="http://localhost:18081/user/list";
String json=restTemplate.getForObject(url,String.class);
System.out.println(json);
}
}
要先在起步引导类中创建Restemplate的Bean容器,也就是在服务消费者使用ResTemplate调用服务提供者,使用ResTemplate调用的时候,需要先创建并注入到SpringIOC容器中
@SpringBootApplication
public class UserConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(UserConsumerApplication.class,args);
}
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
SpringDataJPA
SpringBoot框架中为我们提供了实现类的注解,所有大部分情况我们都不再需要写sql语句
- @Entity 表示业务实体 标识这是一个Spring要创建的Bean
- @Table (name=“数据库表名”) 映射表名
- @Column(name=" ") 如果数据库字段名与对象名不相同时,要加上此注解,并指定数据库字段名
- @Transient 排除数据库字段,当一个普通java对象属性使用
- @Id 映射当前属性是主键
- @GeneratedValue (strategy=GenerationType.IDENTITY) 主键自增
@Entity //表示业务实体,标识这是spirng要创建的javaBean类
@Table(name = "tb_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;//主键id
private String username;//用户名
private String password;//密码
private String name;//姓名
private Integer age;//年龄
private Integer sex;//性别 1男性,2女性
private Date birthday; //出生日期
private Date created; //创建时间
private Date updated; //更新时间
private String note;//备注
//Get和Set方法
注意:Dao接口继承JpaRespository<T,Id> T: 目标类(实现类) id:主键类型
public interface UserDao extends JpaRepository<User,Integer> {
}
注册中心 Eureka
运行原理
-
ApplicationServicet 服务提供者注册到EurkeServer(注册服务名字) 服务提供者会通过http方式定时向注册中心发送心跳信息(默认90S),表示服务存活
-
ApplicationClient 服务消费者在启动的那一刻,会向注册中心调用需要的服务名单,拿到名单后直接通过ip和地址向服务提供者进行数据调用
-
EurekaServer注册中心会定时向客户端消费者发送生产者名单(默认30S)
注册中心的搭建
<!--eureka-server依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
application.yml
server:
port: 7001
spring:
application:
name: eureka-server
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://localhost:7001/eureka
在起步引导类中加入@EnableEurekaServer 注解
@SpringBootApplication
@EnableEurekaServer //开启Eureka服务
public class EurekaServerApplication {public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class,args); }}
生产者完成注册
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
application.yml 配置文件
server:
port: 18081
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url:
jdbc:mysql://192.168.13.100/test?seUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
application:
name: user-provider #服务的名字,不同的应用,名字不同,如果是集群,名字需要相同
#指定eureka服务地址
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
在起步引导类中使用注解开启客户端发现功能 @EnableDiscoveryClient
@EnableEurekaClient //开启客户端发现功能
起步引导类要加上 (二选一)
- @EnableEurekaClient 仅支持 Eureka
- @EnableDiscoveryClient 不仅支持Eureka,还可以支持zookeeper
消费者完成注册
@RequestMapping(value = "/{id}")
public User queryById(@PathVariable(value = "id")Integer id){
//根据服务名获取到实体数据集合
List<ServiceInstance> instances = discoveryClient.getInstances("user-provider");
ServiceInstance serviceInstance = instances.get(0);
//此时获取到的host是主机名,注意:要是想使用ip地址,必须再在服务提供者的配置文件中配置instance属性
String url="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/user/find/"+id;
return restTemplate.getForObject(url,User.class);
}
**注意: 要是想使用ip地址,必须再在服务提供者的配置文件中配置instance属性 **
#指定eureka服务地址
eureka:
client:
service-url:
# EurekaServer的地址
defaultZone: http://localhost:7001/eureka
instance:
#指定IP地址
ip-address: 127.0.0.1
#访问服务的时候,推荐使用IP
prefer-ip-address: true
Eureka配置详解
服务注册
- 当我们开启了客户端发现注解@DiscoveryClient 同时导入 enureka-client依赖坐标
- 同时配置Eureka服务注册中心地址在配置文件中
- 服务在启动时,会检测是否有@DiscoveryClient 注解和配置信息
- 如果有,则会向注册中心发起注册请求,携带服务元数据信息(IP, 端口等)
- Eureka 注册中心会把服务的信息保存在Map中
服务续约
-
服务注册完成以后,服务提供者会维持一个心跳,保存服务处于存在状态.这个称之为服务续约(renew)
#租约到期,服务失效时间,默认值90秒 lease-expiration-duration-in-seconds: 150 #租约续约间隔时间,默认30秒 lease-renewal-interval-in-seconds:30
服务超过90秒没有发生心跳,EurekaServer会将服务从列表中移除, 前提是EurekServer关闭了自我保护
获取服务列表
服务消费者启动时,会检测是否获取服务注册信息配置,如果是,则会EurekaServer 服务列表获取只读备份,缓存到本地,每隔30秒,会重新获取并更新数据, 时间可以通过registry-fetch-interval-seconds修改
#每隔30秒获取服务列表(只读备份)
registry-fetch-interval-seconds: 30
失效剔除
server:
#服务中心每隔一段时间(默认60秒) 将清单中的没有续约的服务剔除,单位是毫秒
eviction-interval-timer-in-ms: 5000
自我保护
Eureka 会统计服务实例最近15分组心跳续约的比例是否低于85%,如果低于,则会触发自我保护机制. 服务中心页面会显示提示信息
- 自我保护模式下,不会剔除任何服务实例
- 自我保护模式保证了大多数服务依然可用
- 通过enable-self-preservation 配置可以关停自我保护
#关闭自我保护功能,默认是打开的
enable-self-preservation: false
Spring Cloud Ribbon 负载均衡
Ribbon简介
Ribbon是Netflix发布的负载均衡器,有助于控制Http客户端行为. 为Ribbon配置服务提供者地址列表后,Ribbon就可以基于负载均衡算法,自动帮助服务消费者请求.
Ribbon默认提供的负载均衡算法: 轮循,随机,重试法,加权 还可以自定义一负载均衡算法
入门使用
在消费者的ResTemplate容器方法上加上@LoadBalanced注解 表示客户端开启负载均衡 (无需引入依赖)
@Bean
@LoadBalanced //开启负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();}}
修改消费者的调用方式,不再需要手动获取Ip和端口,改为通过服务名调用
@RequestMapping(value = "/{id}")
public User queryById(@PathVariable(value = "id")Integer id){
/* List<ServiceInstance> instances = discoveryClient.getInstances("user-provider");
ServiceInstance serviceInstance = instances.get(0);
String url="http://"+serviceInstance.getHost()+":"+serviceInstance.getPort()+"/user/find/"+id;*/
//使用负载均衡后就不需要再手动获取ip和端口,而是直接通过服务名称调用
String url="http://user-provider/user/find/"+id;
return restTemplate.getForObject(url,User.class);
}
负载均衡策略配置
在消费者中配置更该轮循策略 (真实环境下只选其一)
# 修改服务地址轮询策略,默认是轮询,配置之后变随机
user-provider:
ribbon:
#轮询
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
#随机算法
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
#重试算法,该算法先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用的服务
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule
#加权法,会根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信息足够会切换到自身规则。
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.ZoneAvoidanceRule
Ribbo负载均衡原理
负载均衡器动态的从服务中心获取到服务提供者的访问地址(host,port),LoadBalancerInterceptor这个类会对ResTemplate的请求进行拦截,然后从Eureka根据服务id获取服务列表,随后利用负载均衡算法得到真正服务地址信息,替换服务id
Spring Cloud Hystrix 熔断器
- Hystrix也是Netflix公司的一款组件
- Hystrix是Netflix开源的一个延迟和容错库,用于隔离访问远程服务、第三方库、防止出现级联失败也就是雪崩效应
- Hystrix解决雪崩问题的手段,主要是通过服务降级(兜底),线程隔离
熔断器状态有三种
- closed: 关闭状态 所有请求正常访问
- Open: 打开状态 所有请求都会被降级
- Half Open: 半开状态 Open状态不是永久,打开一会后会进入休眠时间(默认5秒). 休眠时间过后进入半开状态
- 半开状态: 熔断器会判断下一次请求的返回状况,如果成功,熔断器切回Closed状态.如果失败,熔断器切回open状态
熔断器的核心
- 线程隔离: 是指Hystrix 为每一个依赖服务调用一个小的线程,如果线程池用尽,调用立即被拒绝,默认不采用排队
- 服务降级(兜底方法): 优先保证核心服务,而非核心服务不可用或弱可用.触发Hystrix服务降级的情况: 线程池已满,请求超时
快速入门
添加依赖
<!--熔断器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
在消费者的起步引导类中添加@EnableCircuitBreaker 注解 表示开启熔断
@EnableCircuitBreaker //开启熔断
在Contoller层添加方法
/**
* 服务降级处理方法
* @param id
* @return
*/
public User failBack(Integer id){
User user=new User();
user.setUsername("服务降级,默认处理");
return user;
}
在Controller的Servlet入口方法上添加注解@HystrixCommand(fallbackMethod=" 服务降级处理方法名")
@RequestMapping(value = "/{id}")
@HystrixCommand(fallbackMethod = "failBack") //方法如果处理出问题,就调用降级处理方法
public User queryById(@PathVariable(value = "id")Integer id){...}
其它熔断配置
- 熔断后休眠时间: sleepWindowInMilliseconds
- 熔断触发最小次数: requestVolumeThreshold
- 熔断触发错误比列阈值: errorThresholdPercentage
- 熔断超时时间: timeoutInMilliseconds
配置熔断策略
hystrix:
command:
default:
circuitBreaker:
#强制打开熔断器,默认false关闭的,测试配置是否生效,打开后,所有的请求都会被拒绝
forceOpen: false
#熔断最小请求次数,默认值是20,允许同时请求的线程数
requestVolumeThreshold: 10
#触发熔断错误比列阈值,默认值百分之50 例如:如果上面是10,经过参数后是5
errorThresholdPercentage: 50
#熔断后休眠时长,默认五秒
sleepWindowInMilliseconds: 10000
execution:
isolation:
thread:
#熔断超时时间 默认1秒
timeoutInMilliseconds: 1000
SpringCloudFeign
- 集成了Ribbbon的负载均衡
- 集成了Hystrix的熔断器功能
- 支持请求压缩
- 大大简化了远程调用的代码,同时还增强了
- Feign以更加优雅的方式编写远程调用代码,并简化重复代码
导入依赖
<!--配置feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
编写客户端接口
/** 用户信息Feign客户端
*主要是包装生产者请求地址,输入与输出
**/
@FeignClient(value="user-provider") //绑定生产者的服务名称(Eureka中的名称)
@RequestMapping("user")//将生产者的请求url与方法原样配置过来
public interface UserClient{
//注意: 此处的请求地址要和生产者的请求地址一样
@RequestMapping("find/{id}")
public User findById(@PathVariable(value="id")Inteager id);
}
Feign会通过动态代理帮我们生成实现类, 注解@FeignClient声明Feign的客户端,属性value指明服务名称,接口定义的方法,采用springMVC的注解.Fegin会根据注解帮我们生成URL地址
在控制层中修改@RequestMapping
@RestController
@RequestMapping("feign")
public class ConsumerFeignController {
@Autowired
private UserClient userClient;
@RequestMapping("{id}")
public User findById(@PathVariable(value = "id")Integer id){
return userClient.findByUserId(id);
}
}
在起步引导类中开启Feign功能的注解
@EnableFeignClients //开启Feign客户端
Feign是默认支持负载均衡的 (随机)
Feign内置的ribbon默认设置了请求超时时长,默认是1000,可以修改 ribbon内部有重试机制,一旦超时,会自动重新发起请求。如果不希望重试可以关闭配置
# 修改服务地址轮询策略,默认是轮询,配置之后变随机
user-provider:
ribbon:
#轮询
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
ConnectTimeoutt: 10000 #连接超时时间
ReadTimeout: 2000 #数据去读超时时间
MaxAutoRetries: 1 #最大重试次数(第一个服务)
MaxAutoRetriesNextServer: 0 #最大重试下一个服务次数(集群情况下)
OkToRetryOnAllOperations: false #无论请求超时或者其它,都进行重试 一般设置为false,不用管
开启Feign熔断机制
在配置文件中开启feign熔断支持,默认是关闭的
feign:
hystrix:
enable: true #开启Feign的熔断功能
创建熔断降级类 @Component注解修饰的类,类继承自定义处理类
请求压缩
SpringCloudFeign支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗
配置文件
feign:
hystrix:
enable: true #开启Feign的熔断功能
compression:
reqeust:
enable: true #开启请求
response:
enable: true #开启响应压缩
Feign的日志级别配置
开启日志级别配置
#配置日志输出级别
logging:
level:
com.itheima.debug
创建日志的实现类
@Configuration
public class FeignConfig{
//设置日志级别
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
- NONE: 不记录任何日志 默认
- BASIC: 记录请求的方法,URL以及响应状态和执行时间
- HEADERS:在BASIC基础上,额外记录了请求和响应的头信息
- FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据
指定配置类
@FeignClient(value="user-provider", //负载均衡
fallback=UserClientFallback.class, //配置熔断器
configuration=FeignConfig.class) //配置日志级别
public interface UserClient {
//注意:此处的请求地址一定要和生产者请求的地址一样
@RequestMapping("/user/find/{id}")
public User findById(@PathVariable("id")Integer id);
}
Spring Cloud Gateway 网关
网关的三大技术体系
- 路由 路由转发
- 过滤 在网关中检测登录验证
- 限流 针对单个微服务限制流量策略
入门
配置依赖
<!--网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
创建启动引导类
@SpringBootApplication
@EnableDiscoveryClient //开启客户端Eureka发现功能
public class GatewayApplication{
public static void main(String[] args){
SpringApplication.run(GatewayApplication.class,args);
}
application.yml 配置文件
server:
prot: 18084
spring:
application:
name: api-gateway
cloud:
gatewat:
routes:
#id唯一标识,可自定义
- id: user-server-route
#动态路径路由 lb(loadBalanced)协议 负载均衡
#lb://微服务应用名称(spring.application.name)
uri: lb://user-provider
#路由的断言,路由拦截规则
predicates:
-Path=/user/**
# Eureka服务中心配置
eureka:
client:
service-url:
# 注册Eureka Server集群
defaultZone: http://127.0.0.1:7001/eureka
过滤器
过滤器作为Gateway的重要功能,常用于请求鉴权,服务调用时长统计,修改请求或响应heder,限流,去除路径等
常见的过滤器
- AddRequestHeader 对匹配上的请求加上 Header
- AddRequestParameters 对匹配上的请求路径
- AddResponseHeader 对从网关返回的响应添加Header
- StripPrefix 对匹配上的请求路径去除前缀
全局过滤器配置
spring:
application:
name: api-gateway # 应用名
cloud:
gateway:
routes:
#id唯一标识,可自定义
- id: user-service-route
#路由地址如果通过lb协议加服务名称时,会自动使用负载均衡访问对应服务
uri: lb://user-provider
# 路由断言:路由拦截规则
predicates:
- Path=/user/**
#默认过滤器配置
default-filters:
# 往响应过滤器中加入响应头信息
- AddResponseHeader=X-Response-Default-MyName,LiZhengKai
AddResponseHeader=small-girl,Jenny
添加 / 去掉前缀
#filters与predicates平级 二选一
predicates:
- Path=/**
filters:
#添加前缀
- prefixPath=/user # /user是前缀
#去掉前缀
- StripPrefix=1 #去除前缀过滤器,一个"/"识别为一个路径