目录
项目简介
业务描述
初始架构设计
工程结构
创建父工程
创建文件服务工程
创建客户端服务工程
父工程初始化
文件资源服务实现
添加项目依赖
服务初始化配置
构建项目启动类
Controller逻辑实现
跨域配置实现
客户端工程逻辑实现
添加依赖
构建项目启动类
创建文件上传页面
启动服务访问测试
API网关(Gateway)工程实践
概述
服务调用架构
工程项目结构设计
创建网关工程及初始化
网关跨域配置
启动工程进行服务访问
网关上对文件上传限流
AOP方式 *** 作日志记录
页面描述
添加项目依赖
创建切入点注解
定义切入点方法
定义日志 *** 作切面
AOP 方式日志记录原理分析
项目简介 业务描述
基于Spring Cloud Alibaba解决方案实现文件上传,例如
本次项目实践,整体上基于前后端分离架构,服务设计上基于spring cloud alibaba解决方案进行实现,例如:
说明,为了降低学习难度,这里只做了初始架构设计,后续会逐步基于这个架构进行演进,例如我们会加上网关工程,认证工程等.
参考如下工程结构,进行项目创建,例如:
创建项目父工程用来管理项目依赖.
创建文件服务工程创建用于处理文件上传业务的工程,例如:
创建客户端服务工程创建一个客户端工程,在此工程中定义一些静态页面,例如文件上传页面.
打开父工程的pom.xml文件,添加如下依赖:
文件资源服务实现 添加项目依赖org.springframework.boot spring-boot-dependencies2.3.2.RELEASE pom import org.springframework.cloud spring-cloud-dependenciesHoxton.SR9 pom import com.alibaba.cloud spring-cloud-alibaba-dependencies2.2.6.RELEASE pom import org.projectlombok lombokprovided
在sca-resource工程中添加如下依赖:
服务初始化配置org.springframework.boot spring-boot-starter-webcom.alibaba.cloud spring-cloud-starter-alibaba-nacos-discoverycom.alibaba.cloud spring-cloud-starter-alibaba-nacos-configcom.alibaba.cloud spring-cloud-starter-alibaba-sentinelorg.springframework.boot spring-boot-starter-actuator
在项目的resources目录下创建bootstrap.yml配置文件(假如后续配置信息要写到配置中心配置文件名必须为bootstrap.yml),并添加如下内容:
server: port: 8881 spring: application: name: sca-resource servlet: multipart: max-file-size: 100MB #控制上传文件的大小 max-request-size: 110MB #请求数据大小 resources: #定义可以访问到上传资源的路径 static-locations: file:d:/uploads #静态资源路径(原先存储到resources/static目录下的资源可以存储到此目录中) cloud: nacos: discovery: server-addr: localhost:8848 config: server-addr: localhost:8848 jt: #这里的配置,后续会在一些相关类中通过@Value注解进行读取 resource: path: d:/uploads #设计上传文件存储的根目录(后续要写到配置文件) host: http://localhost:8881/ #定义上传文件对应的访问服务器构建项目启动类
在当前工程中,创建项目启动类,例如:
package com.jt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class FileApplication { public static void main(String[] args) { SpringApplication.run(FileApplication.class, args); } }
类创建以后,启动当前项目,检测是否可以启动成功,是否有配置错误.
Controller逻辑实现定义处理上传请求的Controller对象,例如:
package com.jt.resource.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.util.UUID; @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; } }跨域配置实现
我们在通过客户端工程,访问文件上传服务时,需要进行跨域配置,在服务端的跨域配置中有多种方案,最常见是在过滤器的层面进行跨域设计,例如:
package com.jt.files.config; @Configuration public class CorsFilterConfig { @Bean public FilterRegistrationBean客户端工程逻辑实现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 fBean= new FilterRegistrationBean(new CorsFilter(configSource)); fBean.setOrder(Ordered.HIGHEST_PRECEDENCE); return fBean; } }
本次项目我们的客户端工程基于springboot工程进行设计,项目上线时可以将其静态资源直接放到一个静态资源目录中.
添加依赖在sca-resource-ui工程的pom文件中添加web依赖,例如:
构建项目启动类org.springframework.boot spring-boot-starter-web
package com.jt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ClientApplication { public static void main(String[] args) { SpringApplication.run(ClientApplication .class, args); } }创建文件上传页面
在工程的resources目录下创建static目录(假如这个目录已经存在则无需创建),然后在此目录创建fileupload.html静态页面,例如:
启动服务访问测试文件上载演示
第一步:启动nacos服务(在这里做服务的注册和配置管理)
第二步:启动sca-resource服务,此服务提供文件上传功能
第三步:启动sca-resource-ui服务,此服务为客户端工程,提供静态资源的访问.所有页面放到此工程中.
第四步:打开浏览器,访问sca-resource-ui工程下的文件上传页面,例如:
API网关(Gateway)工程实践 概述
API 网关是外部资源对服务内部资源访问的入口,所以文件上传请求应该首先请求的是网关服务,然后由网关服务转发到具体的资源服务上。
服务调用架构 工程项目结构设计创建网关工程及初始化
第一步:创建sca-resource-gateway工程,例如:
第二步:添加项目依赖,例如:
org.springframework.cloud spring-cloud-starter-gatewaycom.alibaba.cloud spring-cloud-starter-alibaba-nacos-discoverycom.alibaba.cloud spring-cloud-starter-alibaba-nacos-config
第三步:创建配置文件bootstrap.xml,然后进行初始配置,例如:
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
第四步:构建项目启动类,并进行服务启动,检测是否正确,例如:
package com.jt; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ResourceApplication { public static void main(String[] args) { SpringApplication.run(ResourceApplication.class,args); } }网关跨域配置
当我们基于Ajax技术访问网关时,需要在网关层面进行跨域设计,例如:
package com.jt.config; import org.springframework.context.annotation.Bean; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlbasedCorsConfigurationSource; //@Configuration public class CorsFilterConfig { @Bean public CorsWebFilter corsWebFilter(){ //1.构建基于url方式的跨域配置 UrlbasedCorsConfigurationSource source= new UrlbasedCorsConfigurationSource(); //2.进行跨域配置 CorsConfiguration config=new CorsConfiguration(); //2.1允许所有ip:port进行跨域 config.addAllowedOrigin("*"); //2.2允许所有请求头跨域 config.addAllowedHeader("*"); //2.3允许所有请求方式跨域:get,post,.. config.addAllowedMethod("*"); //2.4允许携带有效cookie进行跨域 config.setAllowCredentials(true); source.registerCorsConfiguration("/**",config); return new CorsWebFilter(source); } }
Spring Gateway工程中的跨域设计,除了可以在网关项目中以java代码方式进行跨域过滤器配置,还可以直接在配置文件进行跨域配置,例如:
spring: cloud: gateway: globalcors: #跨域配置 corsConfigurations: '[/**]': allowedOrigins: "*" allowedHeaders: "*" allowedMethods: "*" allowCredentials: true
启动工程进行服务访问
首先打开网关(Gateway),资源服务器(Resource),客户端工程服务(UI),然后修改fileupload.html文件中访问资源服务端的url,例如
let url="http://localhost:9000/sca/resource/upload/";
接下来进行访问测试,例如:
启动工程进行服务访问
首先打开网关(Gateway),资源服务器(Resource),客户端工程服务(UI),然后修改fileupload.html文件中访问资源服务端的url,例如
let url="http://localhost:9000/sca/resource/upload/";
接下来进行访问测试,例如:
第一步:在网关pom文件中添加依赖
com.alibaba.cloud spring-cloud-starter-alibaba-sentinelcom.alibaba.cloud spring-cloud-alibaba-sentinel-gateway
第二步:在网关配置文件中添加sentinel配置
sentinel: transport: dashboard: localhost:8180 eager: true
第三步:在网关项目启动时,配置jvm启动参数,例如:
-Dcsp.sentinel.app.type=1
第四步:先执行一次上传,然后对上传进行限流规则设计
第五步:修改文件上传页面js,对限流结果进行处理,例如:
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); }) }AOP方式 *** 作日志记录 页面描述
在实现文件上传业务时,添加记录日志的 *** 作.
添加项目依赖在sca-resource工程中添加AOP依赖,例如:
创建切入点注解org.springframework.boot spring-boot-starter-aop
我们项目要为目标业务实现功能增强,锦上添花,但系统要指定谁是目标业务,这里我们定义一个注解,后续用此注解描述目标业务。
package com.jt.resource.annotation; 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 ""; }定义切入点方法
通过上面定义的注解RequiredLog,对sca-resources工程中的ResourceController文件上传方法进行描述,例如:
@RequiredLog("upload file") @PostMapping("/upload/") public String uploadFile(MultipartFile uploadFile) throws IOException {...}
说明:通过@RequiredLog注解描述的方法可以认为锦上添花的“锦”,后续添花的行为可以放在切面的通知方法中。
定义日志 *** 作切面在AOP编程设计中,我们会通过切面封装切入点(Pointcut)和扩展业务逻辑(Around,…)的定义,例如:
package com.jt.resource.aspect; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Slf4j @Aspect @Component public class LogAspect { //定义切入点 @Pointcut("@annotation(com.jt.resource.annotation.RequiredLog)") public void doLog(){}//锦上添花的锦(注解描述的方法) //定义扩展业务逻辑 @Around("doLog()") //@Around("@annotation(com.jt.resource.annotation.RequiredLog)") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { log.debug("Before {}",System.currentTimeMillis()); Object result=joinPoint.proceed();//执行执行链(其它切面,目标方法-锦) log.debug("After {}",System.currentTimeMillis()); return result;//目标方法(切入点方法)的执行结果 } }AOP 方式日志记录原理分析
我们在基于AOP方式记录用户 *** 作日志时,其底层工作流程如下:
说明:当我们在项目中定义了AOP切面以后,系统启动时,会对有@Aspect注解描述的类进行加载分析,基于切入点的描述为目标类型对象,创建代理对象,并在代理对象内部创建一个执行链,这个执行链中包含拦截器(封装了切入点信息),通知(Around,…),目标对象等,我们请求目标对象资源时,会直接按执行链的顺序对资源进行调用。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)