SpringCloud
1.认识微服务:
微服务是一种经历良好设计的分布式架构方案:
服务器架构特征:
单一责任:每个服务按照业务进行颗粒划分;
面向服务:对外提供接口,服务之间通过轻量级机制 通过REST
技术独立:可以使用不同语言,不同的技术开发
数据独立:拥有独立的数据库,可以使用不同的数据存储技术
部署独立:服务可以独立部署,独立扩展,服务之间不影响
微服务技术对比:
组件:
注册发现:Eureka Nacos Consul
统一配置:SpringConfig Nacos
远程服务调用:OpenFeign Dubbo
服务链路监控:Zipkin Sleuth
统一网关路由:SpringCloudGetWay Zuul
流控 降级 保护:Hystix Sentinel
2.Eureka
创建Eureka服务端
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>4.1.1</version>
</dependency>
//开启注解
@EnableEurekaServer
配置
server:
port: 10086
spring:
application:
name: EurekaServer
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka/
其他服务模块注册
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
客服端
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
spring:
application:
name: userService
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 303030
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mapper/*.xml
server:
port: 8081
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka/
@GetMapping("/order/{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
// 根据id查询订单并返回
Order order = orderService.getById(orderId);
//这里主机地址直接改为服务器名
String url="http://userService/user/"+order.getUserId();
User user = restTemplate.getForObject(url, User.class);
order.setUser(user);
return order;
}
}
3.LoadBalancer负载均衡
主要功能客户端的负载均衡算法和服务调用
导入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
在RestTemplate组件上添加@LoadBalanced注解
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
原理:
订单服务首先发送请求给loadBalancer负载均衡组件,在loadBalancer中,有一个拦截器LoadBalancerInterceptor,会拦截RestTemplate发送的HTTP请求,loadBalancer首先根据地址中的服务名从Eureka注册中心拉去服务器列表,然后使用一定的算法(轮询和随机)从列表中选择一个地址进行远程调用。
算法
可以看到在该组件中默认采用的是轮询算法(RoundRobinLoadBalancer),在loadBalancer中,提供了两种算法:RoundRobinLoadBalancer(轮询)和RandomLoadBalancer(随机)
切换负载均衡模式
@Configuration
public class CustomLoadBalancerConfiguration {
@Bean
ReactorLoadBalancer<ServiceInstance> loadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
//返回随机轮询负载均衡方式
return new RandomLoadBalancer(loadBalancerClientFactory.
getLazyProvider(name, ServiceInstanceListSupplier.class),
name);
}
}
开启定义的负载均衡模式
@SpringBootApplication
@LoadBalancerClients(
{@LoadBalancerClient(name = "userService",configuration = CustomLoadBalancerConfiguration.class)
}
)
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
4.Feign远程调用
使用RestTemplate方式调用存在问题
可读性差 URL维护麻烦传递参数不方便
代码不够优雅
Feign
是一个声明式
的Web服务客户端,它让编写Web服务客户端变得更加简单。我们不用再写一堆复杂的代码来处理HTTP请求,只需要通过简单的接口和注解
,就能完成服务间的调用。
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
开启注解
@EnableFeignClients
声明
@FeignClient(name = "userService")
public interface UserServiceClient {
//通过@GetMapping注解定义调用的具体路径
@GetMapping("/user/{id}")
User getUserById(@PathVariable Long id);
}
使用
@GetMapping("/order/{orderId}")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) {
// 根据id查询订单并返回
Order order = orderService.getById(orderId);
//这里主机地址直接改为服务器名
// String url="http://userService/user/"+order.getUserId();
// User user = restTemplate.getForObject(url, User.class);
User user = userServiceClient.getUserById(order.getUserId());
order.setUser(user);
return order;
}
日志配置查看每次远程调用日志
- 首先在application.yml配置日志记录器级别(SpringBoot默认使用的是logback日志框架)
logging:
level:
com.cg.service.UserServiceClient: DEBUG
2.修改Feign组件中的日志级别,不同的级别记录的日志内容不同
-
NONE ,不记录(默认)。
-
BASIC ,仅记录请求方法和URL以及响应状态码和执行时间。
-
HEADERS ,记录基本信息以及请求和响应标头。
-
FULL ,记录请求和响应的标头、正文和元数据。
@Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel() { // 设置日志级别为FULL,记录请求和响应的头信息、正文和元数据 return Logger.Level.FULL; } }
饥饿模式
通过接口调用,发现第一次请求服务的耗时很长(80ms左右),后面再请求只需要很短的时间(10ms左右),这是因为LoadBalancer组件默认采用懒加载
,即第一次访问去创建服务实例(userservice),并且,可以通过配置饥饿加载
让SpringBoot启动的时候就创建服务实例。
spring:
cloud:
loadbalancer:
eager-load:
clients: userservice
5.服务网关
在Gateway服务网关中提供了两大功能:
- 路由转发:通过配置网关路由,将外界请求转发到微服务中。
- 过滤器:通过配置过滤器可以过滤用户的请求和相应,实现身份认证、权限校验和限流等功能。
路由转发:
创建一个新的模块
1.依赖
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka客户端 同样需要注册到eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.配置
eureka:
client:
service-url:
defaultZone: http://localhost:10086/eureka/
server:
port: 80
spring:
application:
name: gateway
cloud:
##配置路由规则
gateway:
routes:
- id: userService # 路由id,自定义,只要唯一即可
uri: lb://userService # 路由的目标地址,lb前缀表示从注册中心获取服务实例,lb后接服务注册中心注册的服务名称
predicates: ##路由断言,判断请求是否符合路由规则
- Path=/user/** # 定义路由路径匹配规则,只要以/user/开头就符合要求
- id: orderService
uri: lb://orderService
predicates:
- Path=/order/**
3.使用
在浏览器输入localhost/user/101 自动找到orderServer下的接口
GateWay过滤器 GatewayFilter
是网关中一种过滤器 可以对进入网关的请求 和服务器返回响应处理
提供了31种不同的路由过滤器工厂
spring:
cloud:
gateway:
#配置路由规则
routes:
- id: userservice # 路由id,自定义,只要唯一即可
uri: lb://userservice # 路由的目标地址,lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,判断请求是否符合路由规则
- Path=/user/** # 定义路由路径匹配规则,只要以/user/开头就符合要求
#配置过滤器
filters:
# 拦截请求,为所有访问userservice服务的请求添加请求头(info:Call userservice successful)
- AddRequestHeader=info,Call userservice successful
- id: orderservice
uri: lb://orderservice
predicates:
- Path=/order/**
Gateway跨域问题
只是浏览器策略
spring:
cloud:
gateway:
globalcors: # 全局的跨域处理
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://127.0.0.1:8848"
- "http://www.xxx.com"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
父子工程
在父工程中只保留pom.xml文件,因为父工程只是作为项目聚合和依赖传递使用,不会产生业务数据以及相关配置
修改 Parent 项目 Pom.xml 的 packaging 标签打包方式为 pom
packaging 包含三个值 Jar、War、Pom,默认 Jar的方式
首先来解释下 packaging 为 Pom 的意思,宏观而言即没有代码需要测试或者编译,也没有资源需要处理
- Jar: 内部调用或作为服务进行发布使用
- War: 需要部署的项目
- Pom: 寓意为一个父级项目,一般作为项目聚合和依赖传递使用
<!--packaging 标签打包方式为 pom,寓意为一个父级项目,一般作为项目聚合和依赖传递使用-->
<packaging>pom</packaging>
<modules>
<module>user-service</module>
</modules>
修改子项目
<parent>
<groupId>org.cg</groupId>
<artifactId>springcloud-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
首先来解释下 packaging 为 Pom 的意思,宏观而言即没有代码需要测试或者编译,也没有资源需要处理
- Jar: 内部调用或作为服务进行发布使用
- War: 需要部署的项目
- Pom: 寓意为一个父级项目,一般作为项目聚合和依赖传递使用
<!--packaging 标签打包方式为 pom,寓意为一个父级项目,一般作为项目聚合和依赖传递使用-->
<packaging>pom</packaging>
<modules>
<module>user-service</module>
</modules>
修改子项目
<parent>
<groupId>org.cg</groupId>
<artifactId>springcloud-parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>