一、创建初始工程结构
创建项目
父工程pom文件添加依赖
- springboot
- springcloud
- springcloudAlibaba
- lombok
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
二、子工程resouce
1.子工程resouce添加pom依赖
<dependencies>
<!--Spring Boot Web (服务-内置tomcat)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Nacos Discovery (服务注册发现)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--Nacos Config (配置中心)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--Sentinel (流量防卫兵-限流和熔断)-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--Spring Boot 监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--spring boot aop-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--资源服务器权限控制,不做认证-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
2.子工程resource配置文件
server:
port: 8881
spring:
application:
name: sca-resource
servlet:
multipart:
max-file-size: 100MB #控制上传文件的大小
max-request-size: 110MB #请求数据的大小
resources:
static-locations: file:d:/SpringCloudImages #静态资源的存储目录,默认是resource-static
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
# 自定义 后面会用@value注解读取
jt:
resource:
path: d:/SpringCloudImages #设计上传文件存储的根目录(后续要写到配置文件)
host: http://localhost:8881/ #定义上传文件对应的访问服务器
3.创建启动类
@SpringBootApplication
public class FileApplication {
public static void main(String[] args) {
SpringApplication.run(FileApplication.class, args);
}
}
4.定义Controlelr类
之前做过,不做解释
@Slf4j
@RestController
@RequestMapping("/resource/")
public class ResourceController {
//当了类的上面添加了@Slf4J就不用自己创建下面的日志对象了
// private static final Logger log=
// LoggerFactory.getLogger(ResourceController.class);
@Value("${jt.resource.path}")
private String resourcePath;//="d:/uploads/";
@Value("${jt.resource.host}")
private String resourceHost;//="http://localhost:8881/";
@PostMapping("/upload/")
public String uploadFile(MultipartFile uploadFile) throws IOException {
//1.创建文件存储目录(按时间创建-yyyy/MM/dd)
//1.1获取当前时间的一个目录
String dateDir = DateTimeFormatter.ofPattern("yyyy/MM/dd")
.format(LocalDate.now());
//1.2构建目录文件对象
File uploadFileDir=new File(resourcePath,dateDir);
if(!uploadFileDir.exists())uploadFileDir.mkdirs();
//2.给文件起个名字(尽量不重复)
//2.1获取原文件后缀
String originalFilename=uploadFile.getOriginalFilename();
String ext = originalFilename.substring(
originalFilename.lastIndexOf("."));
//2.2构建新的文件名
String newFilePrefix=UUID.randomUUID().toString();
String newFileName=newFilePrefix+ext;
//3.开始实现文件上传
//3.1构建新的文件对象,指向实际上传的文件最终地址
File file=new File(uploadFileDir,newFileName);
//3.2上传文件(向指定服务位置写文件数据)
uploadFile.transferTo(file);
String fileRealPath=resourceHost+dateDir+"/"+newFileName;
log.debug("fileRealPath {}",fileRealPath);
//后续可以将上传的文件信息写入到数据库?
return fileRealPath;
}
}
5.进行跨域配置
原先的跨域使用的是@CrossOrigin注解方式,但是它也有弊端,只在controller层处理跨域,微服务项目会被拦截器拦截,走不到controller,所以这个也就没用了,我们需要创建一个配置类在过滤器层面进行跨域配置
package com.jt.files.config;
/**
* 跨域配置(基于过滤器方式进行配置,并且将过滤优先级设置高一些)
*/
@Configuration
public class CorsFilterConfig {
@Bean
public FilterRegistrationBean<CorsFilter> filterFilterRegistrationBean(){
//1.对此过滤器进行配置(跨域设置-url,method)
UrlBasedCorsConfigurationSource configSource=new UrlBasedCorsConfigurationSource();
CorsConfiguration config=new CorsConfiguration();
//允许哪种请求头跨域
config.addAllowedHeader("*");
//允许哪种方法类型跨域 get post delete put
config.addAllowedMethod("*");
// 允许哪些请求源(ip:port)跨域
config.addAllowedOrigin("*");
//是否允许携带cookie跨域
config.setAllowCredentials(true);
//2.注册过滤器并设置其优先级
configSource.registerCorsConfiguration("/**", config);
FilterRegistrationBean<CorsFilter> fBean= new FilterRegistrationBean(new CorsFilter(configSource));
fBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return fBean;
}
}
基本是固定写法,用时复制粘贴稍作修改即可
三、子工程resource-ui
1.添加pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.创建启动类
@SpringBootApplication
public class ClientApplication {
public static void main(String[] args) {
SpringApplication.run(ClientApplication .class, args);
}
}
3.创建静态资源页面
创建static静态资源目录,创建html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上载演示</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<form id="fileForm" method="post" enctype="multipart/form-data" onsubmit="return doUpload()">
<div>
<label>上传文件
<input id="uploadFile" type="file" name="uploadFile">
</label>
</div>
<button type="submit">上传文件</button>
</form>
</body>
<script>
//jquery代码的表单提交事件
function doUpload(){
//获得用户选中的所有图片(获得数组)
let files=document.getElementById("uploadFile").files;
if(files.length>0){
//获得用户选中的唯一图片(从数组中取出)
let file=files[0];
//开始上传这个图片
//由于上传代码比较多,不想和这里其它代码干扰,所以定义一个方法调用
upload(file);
}
//阻止表单提交效果
return false;
};
// 将file上传到服务器的方法
function upload(file){
//定义一个表单
let form=new FormData();
//将文件添加到表单中
form.append("uploadFile",file);
//异步提交
let url="http://localhost:8881/resource/upload/";
axios.post(url,form)
.then(function (response){
alert("upload ok")
console.log(response.data);
})
.catch(function (e){//失败时执行catch代码块
console.log(e);
})
}
</script>
</html>
四、测试
测试成功,但这个不是我们需要的,我们来继续的完善它
五、添加网关
服务调用架构图
浏览器访问我们的页面资源调用网关再访问我们的资源服务
1.添加网关项目并创建启动类
@SpringBootApplication
public class ResourceApplication {
public static void main(String[] args) {
SpringApplication.run(ResourceApplication.class,args);
}
}
2.添加pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
3.配置配置文件
server:
port: 9000
spring:
application:
name: sca-resource-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
config:
server-addr: localhost:8848
file-extension: yml
gateway:
discovery:
locator:
enabled: true
routes:
- id: router01
uri: lb://sca-resource
predicates:
- Path=/sca/resource/upload/**
filters:
- StripPrefix=1
4.跨域配置
我们之前用java代码写配置类进行跨域,现在我们可以再Gateway的配置类里写跨域配置,并写在nacos中进行动态的跨域配置
5.测试
测试成功,接下来我们继续完善功能
六、网关限流
1.在网关pom中添加依赖
- sentinel
- sentinel-gateway
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
2.配置sentinel
sentinel:
transport:
dashboard: localhost:8180 #Sentinel 控制台地址
port: 8719 #客户端监控API的端口 端口被占用会递增
eager: true #取消Sentinel控制台懒加载,即项目启动即连接
3.配置jvm参数并启动
-Dcsp.sentinel.app.type=1
4.增加流控规则
5.定义限流后页面返回值
function upload(file){
//定义一个表单(axios中提供的表单对象)
let form=new FormData();
//将文件添加到表单中
form.append("uploadFile",file);
//异步提交(现在是提交到网关)
//let url="http://localhost:8881/resource/upload/"
let url="http://localhost:9000/sca/resource/upload/";
axios.post(url,form)
.then(function (response){
alert("upload ok")
console.log(response.data);
})
.catch(function (e){//失败时执行catch代码块
//被限流后服务端返回的状态码为429
if(e.response.status==429){
alert("上传太频繁了");
}
console.log("error",e);
})
}
6.测试限流效果
测试成功,接下来我们继续来完善功能
七、AOP面向切面操作日志
1.在resource里添加AOP的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.创建切入点注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*通过此注解描述需要执行扩展业务逻辑的方法*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredLog {
String value() default "";
}
有此注解的类方法表示要进行AOP
3.定义日志操作切面
/*Aop 设置中的切面对象*/
/*切点(执行拓展业务逻辑的入口 :@Pointcut
通知(封装拓展业务逻辑)) :@Around*/
@Aspect
@Component
@Slf4j
public class LogAspect {
/*定义切入点表达式,在那些地方定义切入点*/
//注解方式
@Pointcut("@annotation(com.jt.annotation.RequiredLog)")
public void doLog(){
}
/*在指定的切入点方法上执行@Around注解描述的方法*/
@Around("doLog()")
public Object doAround(ProceedingJoinPoint jp) throws Throwable {
log.info("Around.Before {}", System.currentTimeMillis());
Object result = jp.proceed(); //执行目标执行连(包含切面,目标方法)
log.info("Around.After {}",System.currentTimeMillis());
/*日志存到数据库*/
return result;
}
}
@Around定义了环绕通知,我们也可以定义其他的,根据业务不同所用的注解也不同
4.在需要AOP的方法上添加注解
@RequiredLog("文件上传")//此注解描述的为切入点方法
@PostMapping("upload")
public String uploadFile(MultipartFile uploadFile) throws IOException {
AOP 方式日志记录原理分析
明:当我们在项目中定义了AOP切面以后,系统启动时,会对有@Aspect注解描述的类进行加载分析,基于切入点的描述为目标类型对象,创建代理对象,并在代理对象内部创建一个执行链,这个执行链中包含拦截器(封装了切入点信息),通知(Around,…),目标对象等,我们请求目标对象资源时,会直接按执行链的顺序对资源进行调用。
明天继续完善功能,进行单点登录实现