springCloud
- springcloud一般常用服务调用方法
- okhttp
- resttemplate
- httpclient
-
eruka
-
zookeeper和eruka
-
CAP定理:分布式系统中这个定理是一定存在的,是无法三项都满足的,只能满足其中的两项
-
p:分区容错性:服务器A在中国,服务器B在美国,A和B需要进行通信【进行数据的交换】,因为网络的原因,有可能导致A和B通信失败
-
a:系统可用性:在商品服务192.168.1.25宕机之后,其它节点可以进行通信,让到商品服务192.168.1.25的请求转发到其他可用的节点,保证系统的可用性。把宕机的节点隔离,保护起来,直到修复后,重新启用
-
c: 数据一致性:张三访问商品服务192.168.1.25,得到的iphone11 9999,李四访问商品服务192.168.1.30,得到的iphone11 9999
-
分区容错无法避免,因此可以认为 CAP 的 P 总是成立。那么应该就是选择 CP 还是 AP ?
-
Zookeeper保证的CP。当Zookeeper集群节点中的Master节点宕机后,其他节点需要通过选举机制,重新选举Leader,选举是需要时间的,在这个时间段内,系统是不可用的。虽然时间不长,但是因为请求的频繁,不能保证在该时间段内,系统是可用的。
-
Eureka保证的是AP。当Eureka集群节点中,有一个节点宕机了,因为Eureka的节点之间是平级的关系,宕机节点的请求会被转发到其他节点上处理,所以保证系统在某个节点或某些节点宕机后,系统依旧可用。
-
创建eruka的服务
-
导入依赖
-
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency> - 引导类
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
- 配置文件
server:
port: 10086
spring:
application:
name: eureka-server
eureka:
client:
service-url:
# eureka 服务地址,如果是集群的话;需要指定其它集群eureka地址
defaultZone: http://127.0.0.1:10086/eureka
# 不注册自己
register-with-eureka: false
# 不拉取服务
fetch-registry: false
- 服务注册和发现
- 添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
- 引导类
//开启Eureka客户端发现功能
@EnableDiscoveryClient
//或者@EnableEurekaClient
- 配置文件
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
server:
port: 10086
eureka:
client:
service-url:
# eureka 服务地址,如果是集群的话;需要指定其它集群eureka地址
defaultZone: http://127.0.0.1:10087/eureka
- 另外在eureka重新创建另一个新的服务配置
server:
port: 10087
eureka:
client:
service-url:
# eureka 服务地址,如果是集群的话;需要指定其它集群eureka地址
defaultZone: http://127.0.0.1:10086/eureka
- 每个节点是同级关系
- eureka的相关配置
- 服务端的相关配置
- eureka:
- client:
- service-url:
- defaultZone:
- instance:
- # 更倾向使用ip地址,而不是host名
- prefer-ip-address: true
- # ip地址
- ip-address: 127.0.0.1
- # 续约间隔,默认30秒
- lease-renewal-interval-in-seconds: 5
- # 服务失效时间,默认90秒
- 消费端的配置
- lease-expiration-duration-in-seconds: 5
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
# 获取服务地址列表间隔时间,默认30秒
registry-fetch-interval-seconds: 10
- 注册中心配置 eureka:
- client:
- service-url:
- defaultZone:
- register-with-eureka: false
- # 不拉取服务
- fetch-registry: false
- server:
- # 服务失效剔除时间间隔,默认60秒
- eviction-interval-timer-in-ms: 60000
- # 关闭自我保护模式(默认是打开的)
- ribbon
- 根据服务名到Eureka注册中心获取服务地址列表,再通过Ribbon负载均衡算法从地址列表中获取一个服务地址并访问
- 轮询和随机
- 原理:是基于拦截器实现的。在执行RestTemplate发送服务地址请求的时候,使用负载均衡拦截器拦截,根据服务名获取服务地址列表,使用Ribbon负载均衡算法从服务地址列表中选择一个服务地址,访问该地址获取服务数据。
- ribbon的使用
- 准备工作:准备两个user-service
- 依赖:因为eureka中已经集成了ribbon,所以无需导入
- 修改引导类:在RedisTemplate的配置方法上加注解 @LoadBalanced
- 修改ConsumerController @GetMapping("{id}")
- enable-self-preservation: false
- public User queryById(@PathVariable("id") Long id){
- String url = "
- + id;
- User user = restTemplate.getForObject(url, User.class);
- return user;
- 负载均衡算法默认使用轮询,可以通过配置实现修改
- }
#注册到eureka中的服务名
user-service:
ribbon:
#负载均衡使用随机方式
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
- Hystrix【熔断器】
- 当请求微服务,请求超时或失败时,如何及时给用户响应?
- 作用:可以在调用服务的时候,在服务出现异常时进行服务降级
避免一直长时间等待服务返回结果而出现雪崩效应 - 服务降级:及时返回服务失败结果:当请求失败时【超时,或者没有响应】,通过降级逻辑返回失败结果,加速响应结果,一旦响应结果,线程就被释放,就不会占用连接
- 服务降级实现
- 在consumer-demo中配置
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 引导类加注解@EnableCircuitBreaker
@SpringBootApplication
//@EnableEurekaClient
@EnableDiscoveryClient //开启Eureka客户端发现功能
@EnableCircuitBreaker //开启熔断
//@SpringCloudApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
- 编写降级逻辑
- 方式一
- 编写降级逻辑方法
#降级逻辑方法,返回值为String
public String queryByIdFallback(Long id){
log.error("查询用户信息失败。id:{}", id);
return "对不起,网络太拥挤了!";
}
- 被监视的方法
- 加注解@HystrixCommand(fallbackMethod = "queryByIdFallback")
@GetMapping("/{id}")
//HystrixCommand 该方法如果响应失败,则需要进行降级处理,调用queryByIdFallback方法进行降级处理
@HystrixCommand(fallbackMethod = "queryByIdFallback")
//@HystrixCommand
public String queryById(@PathVariable Long id){
String url = "http://user-s创建defaultFallback方法ervice/user/" + id;
return restTemplate.getForObject(url, String.class);
}
- 把当前方法的返回值写成跟降级逻辑方法一样的返回值String
- 方式二
- controller类加注解@DefaultProperties(defaultFallback = "defaultFallback")
- 创建defaultFallback方法
- 被监视的方法
@GetMapping("/{id}")
//@HystrixCommand(fallbackMethod = "queryByIdFallback")
@HystrixCommand
public String queryById(@PathVariable Long id){
String url = "http://user-service/user/" + id;
return restTemplate.getForObject(url, String.class);
}
- 超时设置
- 默认请求超时为1秒
- 修改超时时间
hystrix:
command:
default:
execution:
isolation:
thread:
#请求超时时间,默认为1秒
timeoutInMilliseconds: 2000
hystrix:
command:
default:
circuitBreaker:
# 触发熔断错误比例阈值,默认值50%
errorThresholdPercentage: 50
# 熔断后休眠时长,默认值5秒
sleepWindowInMilliseconds: 10000
# 熔断触发最小请求次数,默认值是20
requestVolumeThreshold: 10
- feign
- Feign是一个声明式WebService客户端
- Web Service技术, 能使得运行在不同机器上的不同应用无须借助附加的、专门的第三方软件或硬件, 就可相互交换数据或集成。
- 可以理解为Fegin就是一个controller,只不过里面的方法没有实现,而提供了url,在调用时会真正的去调用目标controller中能够匹配该url的方法【动态代理】
- 可以理解为使用Fegin来代替RestTemplate实现http调用服务接口【在这里相当于通过Feign调用user-service的controller】
- feign使用:
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 创建Fegin接口
@FeignClient(value = "user-service")
public interface UserClient {
//http://user-service/user/123
@GetMapping("/user/{id}")
User queryById(@PathVariable Long id);
}
- 注意:在这里@PathVariable注解的参数一定要加上【因为idea版本的问题,有可能会报错】
- Caused by: java.lang.IllegalStateException: PathVariable annotation was empty on param 0
- 创建ConsumerFeignController
- 注入UserClient,通过UserClient的代理对象实现http调用
@RestController
@RequestMapping("/cf")
public class ConsumerFeignController {
@Autowired
private UserClient userClient;
@GetMapping("/{id}")
public User queryById(@PathVariable Long id){
return userClient.queryById(id);
}
}
- 引导类标记 @EnableFeignClients开启feign
- 集成Ribbon
- 本身已经集成,直接使用即可
- 配置参数
ribbon:
# 连接超时时长
ConnectTimeout: 1000
# 数据通信超时时长
ReadTimeout: 2000
# 当前服务器的重试次数
MaxAutoRetries: 0
# 重试多少次服务
MaxAutoRetriesNextServer: 0
# 是否对所有的请求方式都重试
OkToRetryOnAllOperations: false
- 集成Hystix
- 开启Hystix
- 配置
feign:
hystrix:
# 开启Feign的熔断功能
enabled: true
- 具体实现
- 编写熔断的处理类
- 当Feign调用http失败时,会进行降级逻辑
@Component
public class UserClientFallback implements UserClient {
@Override
public User queryById(Long id) {
User user = new User();
user.setId(id);
user.setName("用户异常");
return user;
}
}
- 修改Feign客户端
- 在@FeignClient使用fallback属性指定熔断处理类
@FeignClient(value = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
//http://user-service/user/123
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
- 修改user-service的controller
- 让queryById方法休眠两秒,因为设置过超时时间,所以会进入降级逻辑
- feign配置
- 请求压缩:提高请求与响应的速度
- 配置
feign:
compression:
request:
# 开启请求压缩
enabled: true
# 设置压缩的数据类型
mime-types: text/html,application/xml,application/json
# 设置触发压缩的大小下限
min-request-size: 2048
response:
enabled: true # 开启响应压缩
- 记录日志:
日志级别:
NONE:不记录任何日志信息,这是默认值。
BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
- 编写配置类FeignConfig
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
//记录所有请求和响应的明细
return Logger.Level.FULL;
}
}
- 开启记录日志
- 修改Feign客户端,在@FeignClient使用configuration属性指定日志配置类
@FeignClient(value = "user-service", fallback = UserClientFallback.class, configuration = FeignConfig.class)
public interface UserClient {
//http://user-service/user/123
@GetMapping("/user/{id}")
User queryById(@PathVariable("id") Long id);
}
- 配置日志级别
- 注意//开发用debug //生产用info,warn
- application.yml
# 日志级别
logging:
level:
#指定包的日志级别
com.itheima: debug
- GateWay网关
- 问题:请求直接通过Feign调用微服务,如何实现系统的鉴权与限流等问题
- 鉴权:当用户访问目标资源,没有权限时,返回空,告知其没有权限【之前可以使用权限框架或拦截器实现】
- 限流:当有很多请求同时访问目标资源时,为避免压力,可以进行在单位时间内进行请求限制,允许部分请求直接访问,部分请求延迟访问【延迟时间不会太长,用户感知不到】,来避免高并发
- 是一个组件,也是服务【需要注册到eureka中】,由一系列过滤器组成【有很多的内置过滤器】
- 核心功能:路由和过滤
- 路由:配置文件中指定服务名和地址、过滤器、断言 ,动态路由
- 过滤:可以在服务执行之前和之后处理一些非功能性业务,鉴权
- 搭建heima-gateway网关服务
- 引入依赖
-
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
- 引导类
-
@SpringBootApplication @EnableDiscoveryClient public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }
- 配置文件application.yml
- 将符合 Path 规则的一切请求,都代理到 uri 参数指定的地址
- 配置应用名、eureka注册中心地址、路由规则
◾server: port: 10010 spring: application: name: api-gateway cloud: gateway: routes: # 路由id,可以任意【就是一个名称而已】 - id: user-service-route # 代理的服务地址【转发目标服务地址是哪个】 uri: http://127.0.0.1:9091 # lb表示从eureka中获取具体服务 # uri: lb://user-service # 路由断言: 可以匹配映射路径 predicates: # path 是满足以下条件,就会由网关把请求转发到相应的服务 - Path=/user/** eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka instance: prefer-ip-address: true
- 动态路由
- 通过服务应用名调用服务实现路由
- 配置application.yml
server: port: 10010 spring: application: name: api-gateway cloud: gateway: routes: # 路由id,可以任意 - id: user-service-route # lb表示从eureka中获取具体服务 uri: lb://user-service # 路由断言: 可以匹配映射路径 predicates: - Path=/user/** eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka instance: prefer-ip-address: true
- 路由前缀
server: port: 10010 spring: application: name: api-gateway cloud: gateway: routes: # 路由id,可以任意 - id: user-service-route # lb表示从eureka中获取具体服务 uri: lb://user-service # 路由断言: 可以匹配映射路径 predicates: - Path=/** filters: # 添加请求路径的前缀 - PrefixPath=/user eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka instance: prefer-ip-address: true
- 路由后缀
◾server: port: 10010 spring: application: name: api-gateway cloud: gateway: routes: # 路由id,可以任意 - id: user-service-route # lb表示从eureka中获取具体服务 uri: lb://user-service # 路由断言: 可以匹配映射路径 predicates: #- Path=/user/** #- Path=/** - Path=/api/user/** filters: # 添加请求路径的前缀 #- PrefixPath=/user #1表示过滤1个路径,2表示两个路径,以此类推 - StripPrefix=1 eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka instance: prefer-ip-address: true
- 过滤器
- 分类:局部过滤器和全局过滤器
- 局部过滤器:通过 spring.cloud.gateway.routes.filters 配置在具体路由下,只作用在当前路由上
- 全局过滤器:不需要在配置文件中配置,作用在所有的路由上
- 执行生命周期
- pre:请求被执行前调用
- post:请求被执行后调用
- 使用场景
- 请求鉴权:一般 GatewayFilterChain 执行filter方法前,如果发现没有访问权限,直接就返回空
- 异常处理:一般 GatewayFilterChain 执行filter方法后,记录异常并返回
- 服务调用时长统计: GatewayFilterChain 执行filter方法前后根据时间统计
- 配置
- 全局默认过滤器
- application.yml
spring: cloud: gateway: # 默认过滤器,对所有路由都生效 default-filters: #响应头过滤器,对输出的响应设置其头部属性名称为 X-Response-Foo,值为 Bar - AddResponseHeader=X-Response-Foo, Bar
- 自定义局部过滤器
- 编写过滤器【MyGlobalFilter】
@Component public class MyGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { System.out.println("--------------全局过滤器MyGlobalFilter------------------"); String token = exchange.getRequest().getQueryParams().getFirst("token"); if(StringUtils.isBlank(token)){ //设置响应状态码为未授权 exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { //值越小越先执行 return 1; } }
- 配置
- 负载均衡
ribbon: ConnectTimeout: 1000 #请求连接的超时时间 ReadTimeout: 2000 #请求处理的超时时间 MaxAutoRetries: 0 #对当前实例的重试次数 MaxAutoRetriesNextServer: 0 #切换实例的重试次数,譬如从user-service-9091 到 9092
- 服务熔断
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 6000 #熔断超时时间
- 跨域配置
- 浏览器的一种安全措施
- 在js请求访问中,如果访问的地址与当前服务器的域名、ip或者端口号不一致则称为跨域请求。
- 具体实现
- allowedOrigins:指定允许跨域访问服务器的源地址
- allowedMethods:允许跨域的请求方式
- /**:跨域的拦截规则
- 配置文件application.yml
spring: application: name: api-gateway cloud: gateway: globalcors: corsConfigurations: '[/**]': #allowedOrigins: * # 这种写法或者下面的都可以,*表示全部 allowedOrigins: - "http://docs.spring.io" allowedMethods: - GET
- Config【分布式配置中心】
- 在分布式系统中,由于服务数量非常多,配置文件分散在不同的微服务项目中,管理不方便
- 开发有开发的环境,测试有测试的环境,如何测试环境?
- 是一个配置中心服务:需要注册到eureka注册中心,配置文件可以在本地,也可以在远程仓库【GitHub、Gitee】
- 作用:可以获取git仓库中的配置文件,给其它微服务使用,从而使各个微服务的配置在git仓库中进行集中式管理
- 具体实现
- 创建Gitee仓库,创建配置文件【user-dev.yml】,内容就是user-service的application.yml中的配置,配置文件的命名方式{application}-{profile}.yml
- 搭建配置中心服务config-server
- 引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
- 引导类
@SpringBootApplication @EnableConfigServer //开启配置服务 public class ConfigServerApplication { public static void main(String[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
- 配置文件
server: port: 12000 spring: application: name: config-server cloud: config: server: git: # 配置文件在gitee上的地址 uri: https://gitee.com/goheima/heima-config.git eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka
- user-service获取配置中心配置
- 引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> <version>2.1.1.RELEASE</version> </dependency>
- 删除application.yml 或者 把名字改成 xxx.yml 也可以
- 创建 bootstrap.yml
- bootstrap.yml 也是 springboot 能够识别的配置文件,其优先级在 application.yml之前
spring: cloud: config: # 要与仓库中的配置文件的application保持一致 name: user # 要与仓库中的配置文件的profile保持一致 profile: dev # 要与仓库中的配置文件所属的版本(分支)一样 label: master discovery: # 使用配置中心 enabled: true # 配置中心服务名 service-id: config-server eureka: client: service-url: defaultZone: http://127.0.0.1:10086/eureka
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-bus</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency>
- yaml配置
spring: rabbitmq: host: localhost port: 5672 username: guest password: guest management: endpoints: web: exposure: # 暴露触发消息总线的地址 include: bus-refresh
- 修改用户中心服务
- 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-bus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
- bootstrap.yml rabbitmq配置
spring: # 配置rabbitmq信息;如果是都与默认值一致则不需要配置 rabbitmq: host: localhost port: 5672 username: guest password: guest
- UserController类上加注解 @RefreshScope //刷新配置
@RestController @RequestMapping("/user") @RefreshScope //刷新配置 public class UserController { //............. }