Spring顶级项目
)Spring FrameWork(IOC、DI、AOP)
Spring
中Web层
框架,底层基于Servlet
SpringBoot
项目启动时会加载spring-boot-autoconfigure
包下的MTATA-INF
下的spring.factories
文件
import + selector选择器
spring.factories
文件配置了自动装配类的路径
不是所有的自动装配类都生效,是否生效取决于类似的方法上的条件注解@ConditionalOnClass(.class)
条件注解是否生效又取决于pom
文件是否导入了响应的starter
微服务中服务治理一整套解决方案,全家桶(
erueka注册中心
、ribbon负载均衡
、feign远程调用
、gateway网关
)
将业务的所有功能集中在一个项目中开发,打包成一个包部署
根据业务功能对系统进行拆分,每个业务模块作为单独项目开发,称为一个服务
微服务是一种经过良好架构设计的分布式架构方案
SpringCLoud
技术栈、服务接口采用Resuful
风格、服务调用采用Feign
方式SpringCloudAlibaba
技术栈、服务接口采用Restful
风格、服务调用采用Feign
方式SpringCloudAlibaba
技术栈、服务接口采用Dubbo
协议标准、服务调用采用Dubbo
方式Dubbo
老旧技术体系、服务接口采用Dubbo
协议标准、服务调用采用Dubbo
方式远程调用
,rpc框架
,注册中心可以采用zookeeper
、redis
,服务治理相关功能不全
erueka注册中心
、ribbon负载均衡
、feign远程调用
、gateway网关
)微服务中服务治理一整套解决方案,全家桶(
erueka注册中心
、ribbon负载均衡
、feign远程调用
、gateway网关
),SpringCloud
依赖于SpringBoot
SpringCloud
集合了各种微服务功能组件,并基于SpringBoot
实现了这些组件的自动装配,从而提供了良好的开箱即用体验
可以发送HTTP请求
url
中获取一个对象url
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); // 表示这是一个application/x-www-form-urlencoded格式数据
MultiValueMap body = new LinkedMultiValueMap();
// 放入数据
multiValueMap.add();
...
multiValueMap.add();
HttpEntity httpEntity = new HttpEntity(body, headers);
ResponseEntity<Boolean> exchange = new restTemplate.exchange(url, HttpMethod.POST, httpEntity, Boolean.class);
eureka
注册自己的信息eureka
保存这些信息eureka
拉取提供者的信息30秒
向EurekaServer
发送心跳请求,报告健康状态eureka
会更新记录服务列表信息,心跳不正常会被剔除EurekaServer
30秒
向EurekaServer
发送心跳EurekaServer
拉取服务列表spring-cloud-starter-netflix-eureka-server
的依赖<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
@EnableEurekaServer
注解application.yml
文件,编写以下配置server:
port: 10086
spring:
application:
name: eurekaserver
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka/
eureka-client
依赖<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
application.yml
中配置eureka
地址spring:
application:
name: userservice
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka/
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
url
路径,用服务名代替ip、端口
@LoadBalanced
Ribbon的负载均衡规则是一个叫做
IRule
的接口来定义的,每一个子接口都是一种规则
IRule
@Bean
public IRule randomRule() {
return new RandomRule();
}
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Eureka
是客户端负载均衡器: 消费者从Eureka
拉取提供者的实例信息,经过Ribbon
的负载均衡直接发起调用,消费者是知道调用了那个提供者的Nginx
是服务端负载均衡器: 负载均衡能力是在服务端完成的,消费者不知道调用了哪个提供者Ribbon默认采用懒加载,第一次访问的时候才会去创建LoadBalanceClient,请求时间会很长。
饥饿加载会在项目启动时就加载,降低第一次访问的耗时
ribbon:
eager-load:
enabled: true # 开启饥饿加载
clients: userservice # 指定对userservice这个服务饥饿加载
既可以作为注册中心、也可由作为配置中心
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.6.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
<version>2.2.5.RELEASEversion>
dependency>
spring:
cloud:
nacos:
server-addr: localhost:8848
服务 -> 集群 -> 实例
# service1
spring:
cloud:
nacos:
discovery:
cluster-name: HN # 集群1 HN
# service2
spring:
cloud:
nacos:
discovery:
cluster-name: HZ # 集群2 HZ
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡策略,优先选择同级群
Nacos中服务存储和数据存储的最外层都是一个名为namespace的东西,用来做最外层隔离
namespace -> group -> service/data
唯一id
spring:
cloud:
nacos:
discovery:
namespace: 2e32b562-a44c-4750-a2c6-e6539b06438d # 命名空间id
AP方式
,当集群中存在非临时实例时,采用CP方式
;Eureka采用AP方式
spring:
cloud:
nacos:
discovery:
ephemeral: false # 是否是临时实例
Nacos
中添加配置文件# DataId完整格式为${prefix}-${spring.profiles.active}.${file-extension}
# prefix默认为spring.application.name的值,也可以通过配置项spring.cloud.nacos.confg.prefix来配置
# spring.profiles.active为当前环境对应的profile
# file-extension为配置内容的数据格式,可以通过配置项spring.cloud.nacos.config.file.extension来配置,目前只支持properties和yum类型
Nacos
中拉取配置nacos-config
依赖<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>
bootstrap.yaml
(引入bootstrap.yml是在引用application.yml之前 )spring:
application:
name: userservice # 服务名称
cloud:
nacos:
server-addr: localhost:8848 # nacos地址
discovery:
cluster-name: HN
config:
file-extension: yaml # 文件后缀名
prefix: aaa
profiles:
active: dev # 开发环境
@RefreshScope
注解@ConfigurationProperties
注解代替@Value
注解(新建一个类,将远程配置的变量作为成员变量,使用getter
方法取出)nacos
管理配置了服务名和
profiles.active
时,可以同时读取到服务名.ymal
和服务名-profile.ymal
服务名-profile.ymal > 服务名称.ymal > 本地配置
conf
目录下cluster.conf.example
名为cluster.conf
cluster.conf
添加内容# 集群地址和端口,手头没有多于服务器,先放在本地,本地有虚拟机的可以使用192.168.126.1:端口号
127.0.0.1:8845
127.0.0.1:8846
127.0.0.1:8847
application.properties
文件,添加数据库配置spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=123
conf/nginx.conf
文件upstream nacos-cluster {
server 127.0.0.1:8845;
server 127.0.0.1:8846;
server 127.0.0.1:8847;
}
server {
listen 80;
server_name localhost;
location /nacos {
proxy_pass http://nacos-cluster;
}
}
Feign是一个声明式的http客户端,作用是帮我们发送http请求,底层可以理解为RestTemplate+Ribbon
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
@EnableFeignClients(clients = UserClient.class)
// 如果Feign接口包路径和引导类包路径不相同,需要单独指定
@EnableFeignClients(basePackages = "com.example.feignapi") // 目的是创建feign接口代理对象并放入spring容器
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
主要基于SpringMVC
的注解来声明远程调用的信息,比如:
feign:
client:
config:
default:
#不设置connectTimeout会导致readTimeout设置不生效
connectTimeout: 3000
readTimeout: 6000
feign:
client:
config:
userservice: # 针对某个微服务的配置
loggerLevel: FULL # 日志级别
feign:
client:
config:
default: # 针对全局配置
loggerLevel: FULL # 日志级别
# 声明一个类
public class DefaultFeignConfiguration {
@Bean
public Logger.Level.BASIC; // 日志级别为BASIC
}
@EnableFeignClients
这个注解中@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class)
@FeignClient
注解中@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration.class)
Feign底层发起http请求,依赖于其他的框架
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-httpclientartifactId>
dependency>
feign:
client:
config:
default: # 全局配置
loggerLevel: BASIC # 日志级别,BASIC
httpclient:
enabled: true # 开启feign对HttpClient的支持
max-connections: 200 # 最大的连接数
max-connections-per-route: 50 # 每个路径的最大连接数
feign-api
,引入feign
的starter依赖Client
、实体类
、日志配置类
都复制到feign-api
中feign-api
的依赖import
部分,改成导入feign-api
中的包@EnableFeignClients(basePackages = "com.example.feign.clients")
@EnableFeignClients(clients = {UserClient.class})
Spring5
中提供的WebFlux
,属于响应式编程的实现,具备更好的性能Servlet实现
,属于阻塞式编程
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>
server:
port: 10010 # 网关端口
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: localhost: 8848 # nacos地址
gateway:
routes: # 网关路由配置
- id: user-service # 路由id,自定义,只要唯一即可
# uri: http://127.0.0.1:8081 # 路由的目标地址 http就是固定地址,不建议这样写
uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟服务名称
predicates: # 路由断言,也就是判断请求是否符合路由规则的条件
- Path=/user/** # 这个是按照路径匹配,只要以/user/开头就符合要求
filters:
StripPrefix=1 # 去除1个前缀
default-filters: # 默认过滤器
- AddRequestHeader=authorization,2 # 所有的请求头都带有该信息
我们在配置文件中写的断言规则只是字符串,这些字符串会被
Predicate Factory
读取并处理,转变为路由判断的条件
网关路由可以配置的内容包括:
lb
和http
两种GatewayFilter是网关种提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理
defaultFilters
全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样
GatewayFilter
通过配置定义,处理逻辑是固定的,GlobalFilter
的逻辑是自己写代码实现的
GlobalFilter接口
@Order
注解(也可以实现Ordered接口
)@Order(-1) // 数值越小优先级越高
@Component
public class AuthorizeFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 1. 获取请求参数
MultiValueMap<String, String> params = exchange.getRequest().getQueryParams();
// 2. 获取authorization参数
String authorication = params.getFirst("authorization");
// 3. 校验
if (java.util.Objects.equals("admin", authorication)) {
// 放行
return chain.filter(exchange);
}
// 4. 拦截
// 4.1 禁止访问,设置状态码
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
// 4.2 结束处理
return exchange.getResponse().setComplete();
}
}
请求进入网关会碰到三类过滤器: 当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)种,排序后依次执行每个过滤器
Ordered接口
,或者添加@Order
注解来指定order值,由我们自己决定Spring指定
,默认是按照声明顺序从1递增域: 协议、域名、端口
三样有任意一样不一样就是跨域
js
,js
从一个域加载到浏览器,但是发起ajax异步请求请求另一个域的后台,就叫跨域,浏览器默认不允许,有同源策略,为了安全,如果跨域,会报错spring:
cloud:
gateway:
globalcors: # 全局的跨域处理
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 是否允许携带cookie
maxAge: 360000 # 这次跨域检测的有效期
让orgin原来的域和异步请求要访问的域保持一致,不算跨域,然后让Nginx做代理