黑马头条第一天的总结
- 用到的技术栈
- Gateway 网关和ngix的区别?
- Gateway 网关和ngix为什么要一起使用?有什么优势?
- 为什么实体类要实现Serializable接口?
- Spring Cloud Alibaba Nacos作为项目中的注册中心和配置中心,Nacos作用是什么?
- @EnableDiscoveryClient 是什么?
- 全局过滤器实现jwt校验
- 前端项目部署思路
- 在Spring框架中,Ordered接口和GlobalFilter接口用于不同的目的
- StringUtils.isBlank(token)作用是什么?
- 彻底解决Failed to configure a DataSource: ‘url‘ attribute is not specified and no embedded datasource
用到的技术栈
- Spring-Cloud-Gateway : 微服务之前架设的网关服务,实现服务注册中的API请求路由,以及控制流速控制和熔断处理都是常用的架构手段,而这些功能Gateway天然支持
- 运用Spring Boot快速开发框架,构建项目工程;并结合Spring Cloud全家桶技术,实现后端个人中心、自媒体、管理中心等微服务。
- 运用Spring Cloud Alibaba Nacos作为项目中的注册中心和配置中心
- 运用mybatis-plus作为持久层提升开发效率
- 运用Kafka完成内部系统消息通知;与客户端系统消息通知;以及实时数据计算
- 运用Redis缓存技术,实现热数据的计算,提升系统性能指标
- 使用Mysql存储用户数据,以保证上层数据查询的高性能
- 使用Mongo存储用户热数据,以保证用户热数据高扩展和高性能指标
- 使用FastDFS作为静态资源存储器,在其上实现热静态资源缓存、淘汰等功能
- 运用Hbase技术,存储系统中的冷数据,保证系统数据的可靠性
- 运用ES搜索技术,对冷数据、文章数据建立索引,以保证冷数据、文章查询性能
- 运用AI技术,来完成系统自动化功能,以提升效率及节省成本。比如实名认证自动化
- PMD&P3C : 静态代码扫描工具,在项目中扫描项目代码,检查异常点、优化点、代码规范等,为开发团队提供规范统一,提升项目代码质量
Gateway 网关和ngix的区别?
Gateway 网关(如Spring Cloud Gateway)和Nginx
共同点: 1. 都可以作为反向代理服务器和API网关使用
不同点:但它们在设计、功能和使用场景上有一些区别:
- Spring Cloud Gateway 是一个编程式的API网关,并且是响应式的,基于WebFlux框架。
- Nginx 是一个高性能的HTTP服务器和反向代理,使用C语言编写,它是过程式和事件驱动的。Nginx 以其稳定性、高性能和低资源消耗闻名。
Gateway 网关和ngix为什么要一起使用?有什么优势?
-
安全性和隔离:
使用 Nginx 作为最前端的反向代理可以提供额外的安全层。它可以处理外部流量并提供基本的安全防护,如 限流、IP 白名单/黑名单等。
Spring Cloud Gateway 然后处理内部微服务的路由和其他业务逻辑,这样可以将外部流量与内部服务逻辑分离,增强系统的安全性。 -
性能优化:
Nginx 能够高效地处理静态内容、压缩、缓存等,这可以减轻后端网关和服务的负载。
Spring Cloud Gateway 可以专注于处理动态路由、服务发现等微服务相关的操作。 -
灵活的负载均衡:
Nginx 可以作为负载均衡器,将流量分发到多个网关实例,实现跨地域的流量管理和灾难恢复。
Spring Cloud Gateway 可以进一步在微服务层面进行负载均衡,例如基于服务实例的健康状况或响应时间来路由请求。 -
简化的SSL管理:
在 Nginx 层统一管理 SSL 证书,可以简化证书的部署和更新过程。
Spring Cloud Gateway 可以在内部网络中以纯文本方式运行,减少SSL配置的复杂性。 -
协议转换:
Nginx 可以作为协议转换的网关,例如将外部的 HTTPS 请求转换为内部的 HTTP 请求。
Spring Cloud Gateway 则处理微服务层面的通信和协议需求。 -
缓解冷启动问题:
在微服务进行部署或重启时,Nginx 可以提供缓存或静态响应,减少系统冷启动时对用户体验的影响。
Spring Cloud Gateway 在后端服务就绪后再接管流量,确保流量的正确路由。 -
分层的流量控制:
Nginx 可以实现全局的流量控制和防护策略。
Spring Cloud Gateway 可以根据业务需求实现更细粒度的流量控制和路由策。
综合来说,将 Nginx 和 Spring Cloud Gateway 一起使用可以更好地利用各自的优势,为系统提供双层的保护和灵活性。不过,这种架构也会增加系统的复杂性,需要更多的运维知识来确保系统的稳定性和性能。因此,是否采取这种架构需要根据实际业务需求和团队能力来决定。
为什么实体类要实现Serializable接口?
实体类通常建议实现Serializable接口的原因在于Java序列化机制。Serializable接口是Java提供的一个标记(marker)接口,它不包含任何方法,当一个类实现了这个接口,这意味着这个类能够将其实例的状态保存为一系列字节,并且可以在之后完全以相同的状态重新构造出来,即实现了序列化和反序列化的功能。
以下是实现Serializable接口的一些常见原因:
1. 持久化存储:
将实体对象存储到磁盘上,如文件、数据库等,实现对象的持久化。序列化后的数据可以在系统关闭后依然存在,并可以在需要的时候反序列化回原有的对象。
2. 网络传输:
在分布式系统中,经常需要在网络中传输对象。序列化可以将对象转换为字节流,从而可以通过网络发送到另一个系统,然后在另一端将字节流反序列化为对象。
3. 深拷贝:
通过序列化和反序列化可以实现对象的深拷贝,即创建一个内容完全相同但是独立的对象副本,这在某些特定场景下非常有用。
4. Java RMI:
Java远程方法调用(RMI)中,当对象需要在JVM之间传输时,这些对象必须实现Serializable接口,以便它们可以被序列化和反序列化。
5. 缓存对象:
在使用缓存框架如Ehcache、Redis等时,如果需要缓存Java对象,这些对象需要是可序列化的,以便将它们存储在缓存中。
实现Serializable接口的类需要提供一个称为serialVersionUID的静态常数。这个ID作为类的版本号,用于验证序列化的对象和对应类的版本是否匹配。如果一个类在序列化后发生了更改,没有相应更新serialVersionUID,那么反序列化时可能会抛出InvalidClassException。
Spring Cloud Alibaba Nacos作为项目中的注册中心和配置中心,Nacos作用是什么?
-
服务注册与发现(Service Registry and Discovery):
作为服务注册中心,Nacos 允许微服务在启动时将自己的地址(IP + 端口)注册到 Nacos 服务器上。
当服务需要调用其他服务时,它可以查询 Nacos 以获取当前可用的服务实例列表及其地址,从而实现服务之间的动态发现和调用。
这样可以实现服务间的松耦合和负载均衡,并且在服务实例出现变动时无需手动更新配置。
2. 动态配置管理(Dynamic Configuration Management):Nacos 作为配置中心,可以集中管理应用的配置文件。
微服务可以在启动时从 Nacos 获取配置信息,并且在配置发生变更时,Nacos 能够实时推送更新的配置到各个服务实例,避免了重新部署服务的需要。
支持配置的版本管理和回滚,便于跟踪配置变更和快速恢复到旧版本。
3. 服务健康检查(Service Health Check):Nacos 支持服务健康检查,自动检测服务实例的存活状态,并在服务实例不健康时将其从服务列表中剔除,确保服务的可用性和稳定性。
@EnableDiscoveryClient 是什么?
@EnableDiscoveryClient
是一个Spring Cloud提供的注解,用于启用服务发现客户端的功能。当你在Spring Boot应用中添加了这个注解后,应用就具备了服务发现的能力,能够将自己注册到配置好的服务注册中心,比如Eureka、Consul或者Zookeeper等,并且能够从注册中心动态发现其他服务的实例。
服务发现是微服务架构中的一个关键概念,它允许微服务实例在启动时注册到服务注册中心,并在需要时查询其他服务的位置(即IP地址和端口),从而实现服务间的互联互通。这样,服务之间的通信就不再依赖于硬编码的地址,而是基于服务的名称进行动态解析。
以下是一个简单的例子,展示了如何在Spring Boot应用中使用 @EnableDiscoveryClient:
@SpringBootApplication
@EnableDiscoveryClient
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
在这个例子中,MyApp
应用将会注册到服务注册中心,并且能够发现其他注册的服务。
全局过滤器实现jwt校验
思路分析:
-
用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录
-
用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
-
用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN
-
网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误
-
具体实现:
第一:在认证过滤器中需要用到jwt的解析,所以需要把工具类拷贝一份到网 关微服务
第二:在网关微服务中新建全局过滤器:
@Component
@Slf4j
public class AuthorizeFilter implements Ordered, GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取request和response对象
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
//2.判断是否是登录
if(request.getURI().getPath().contains("/login")){
//放行
return chain.filter(exchange);
}
//3.获取token
String token = request.getHeaders().getFirst("token");
//4.判断token是否存在
if(StringUtils.isBlank(token)){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//5.判断token是否有效
try {
Claims claimsBody = AppJwtUtil.getClaimsBody(token);
//是否是过期
int result = AppJwtUtil.verifyToken(claimsBody);
if(result == 1 || result == 2){
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
}catch (Exception e){
e.printStackTrace();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
}
//6.放行
return chain.filter(exchange);
}
/**
* 优先级设置 值越小 优先级越高
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
前端项目部署思路
通过nginx来进行配置,功能如下
- 通过nginx的反向代理功能访问后台的网关资源
- 通过nginx的静态服务器功能访问前端静态页面
配置nginx
①:解压资料文件夹中的压缩包nginx-1.18.0.zip
②:解压资料文件夹中的前端项目app-web.zip
③:配置nginx.conf文件
upstream heima-app-gateway{
server localhost:51601;//这指定了应用程序的运行端口为51601。
}
//server:这是Spring Boot应用的服务配置部分。
server {
listen 8801;
location / {
root D:/workspace/app-web/;
index index.html;
}
location ~/app/(.*) {
proxy_pass http://heima-app-gateway/$1;
proxy_set_header HOST $host; # 不改变源请求头的值
proxy_pass_request_body on; #开启获取请求体
proxy_pass_request_headers on; #开启获取请求头
proxy_set_header X-Real-IP $remote_addr; # 记录真实发出请求的客户端IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #记录代理信息
}
}
④ :启动nginx
在nginx安装包中使用命令提示符打开,输入命令nginx启动项目
可查看进程,检查nginx是否启动
重新加载配置文件:nginx -s reload
⑤:打开前端项目进行测试 – > http://localhost:8801
用谷歌浏览器打开,调试移动端模式进行访问
在Spring框架中,Ordered接口和GlobalFilter接口用于不同的目的
-
Ordered
接口:
Ordered是一个用于定义组件排序顺序的接口。它只有一个方法getOrder(),返回一个整数值表示顺序。在Spring中,多个相同类型的组件(比如Bean、Filter等)可能会同时存在,而它们的执行顺序有时候是很关键的。通过实现Ordered接口,你可以控制这些组件的加载和执行顺序。数值越小,优先级越高,组件就越先执行。在Spring Cloud Gateway中,多个网关过滤器(Filter)可能会处理传入的HTTP请求。实现Ordered接口可以帮助你定义这些过滤器的执行顺序。
-
GlobalFilter
接口:
GlobalFilter是Spring Cloud Gateway中的一个接口,用于创建全局过滤器。全局过滤器会对所有路由的入站请求进行处理。与路由特定的过滤器不同,全局过滤器无需指定特定条件即可应用于所有请求。实现
GlobalFilter
接口的类需要提供filter方法的定义,这个方法定义了过滤器在处理请求时应该执行的逻辑。该方法接收一个ServerWebExchange
和一个GatewayFilterChain
。ServerWebExchange
封装了HTTP请求和响应的相关信息,而GatewayFilterChain
则允许过滤器将处理请求的控制权传递给链中的下一个过滤器。
当你同时实现Ordered和GlobalFilter接口时,你创建的是一个全局过滤器,它会对所有路由生效,并且你可以通过Ordered接口中的getOrder方法来指定其在过滤器链中的执行顺序。
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 过滤器的逻辑,比如修改请求头
return chain.filter(exchange); // 继续执行过滤器链
}
@Override
public int getOrder() {
return -1; // 设置过滤器的优先级,数值越小优先级越高
}
}
在这个示例中,MyGlobalFilter
类定义了一个全局过滤器,并通过实现Ordered
接口来指定其执行顺序。在filter方法中,它可以修改HTTP请求或响应,或者执行任何需要对所有请求进行的全局逻辑。
StringUtils.isBlank(token)作用是什么?
* 如果 token 是 null,返回 true。
* 如果 token 长度为 0,返回 true。
* 如果 token 只包含空白字符(如空格、制表符、换行符等),返回 true。
* 否则,返回 false。
* isBlank 方法和 isEmpty 方法类似,但 isEmpty 只检查字符串是否为 null 或者长度为 0,并不检查空白字符。
彻底解决Failed to configure a DataSource: ‘url‘ attribute is not specified and no embedded datasource
springboot2启动项目报错,应该是数据库连接的问题,导致无法启动。
错误消息如下:
***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).
********************************** 第一种,项目不需要连接数据库,启动报错 ******************************************
解决方法如下:
只要在将@SpringBootApplication修改为@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})就可以启动的时候不需要连接数据库。
@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})
********************************** 第2种,需要连接数据库,启动报错 ******************************************
解决方法如下:
第一种,yml配置示例如下
#在application.properties/或者application.yml文件中没有添加数据库配置信息.
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
第二种,properties文件示例如下
spring.datasource.url = jdbc:mysql://localhost:3306/test?setUnicode=true&characterEncoding=utf8
第三种,mysql的版本不同,示例如下
#mysql8以下的版本,请检查pom.xml文件种依赖的mysql jar包的版本
driver-class-name: com.mysql.jdbc.Driver
#mysql8以下的url写法
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false
#mysql8的版本写法,多了个cj
driver-class-name: com.mysql.cj.jdbc.Driver
#同时mysql8的url也需要加入时区,参照如下
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&tinyInt1isBit=false
第四种,项目没有加载到yml或者properties文件,特别是自己的pom打包是jar的项目,请查看自己的pom.xml文件中的packaging
<packaging>jar</packaging>
第五种,项目使用了**springcloud+nacos
**系列,启动项目时候需要手动指定【–spring.profiles.active=test】,那么在resources文件夹下就必须要有bootstrap-test.yml或者application-test.yml文件,同时配置文件中连接的nacos地址里面也必须配置对应的命名空间,和对应服务名称的yml文件,否则也是报错。下面是配置文件的截图