# notebook-cloud **Repository Path**: tmq777/notebook-cloud ## Basic Information - **Project Name**: notebook-cloud - **Description**: notebook-app的后台服务 自用的笔记系统 - **Primary Language**: Java - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2022-03-19 - **Last Updated**: 2023-02-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: Java, SpringBoot, SpringCloud ## README # notebook-cloud #### 介绍 基于Spring Cloud的笔记本系统**后台服务**。 > [前台工程](https://gitee.com/tmq777/notebook-app)使用vue2.6.14 + element-ui2.15.7搭建 > > 传送门: [notebook-app](https://gitee.com/tmq777/notebook-app) > > 前台界面传送门:[笔记本系统](http://tmq777.gitee.io/notebook-web-app) #### 开发环境 1. jdk 11.0.10 2. idea 3. mysql 8.0.18 4. redis 6.2.6 5. nacos 1.4.3 6. Spring Cloud 2021 7. Spring Cloud Alibaba 2021.0.1.0 版本管理如下: ```xml 11 2021.0.1 2.2.2 1.0.0 com.alibaba.cloud spring-cloud-alibaba-dependencies 2021.0.1.0 pom import org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.mybatis.spring.boot mybatis-spring-boot-starter ${mybatis.version} ``` #### 开发环境部署说明 1. 服务器只暴露file模块端口(最终静态资源使用gitee pages服务托管) 1. 开发时前端使用本地调试 2. 服务器上需要有nacos作为注册中心,目前默认端口18848 3. 服务器上需要有mysql服务,目前默认端口13306 4. 服务器上需要有redis服务,目前使用了redis集群,端口为7001~7006 5. 前台js的baseUrl需要改成服务器的ip `src\util\ApiUrls.js` ```javascript const apis = { api_article: api_article, api_auth: api_auth, api_file: api_file, baseHost: "http://localhost:9000", // 修改ip-网关地址 } ``` 6. 所有服务的数据源配置使用了nacos配置中心进行配置,在配置中心的namespace为notebook,group为common 7. 后台服务需要配置对应的跨域请求允许策略 1. 认证中心`cn/t/notebook/auth/config/SecurityConfig.java` 1. `corsFilter`方法中配置允许的前台请求(认证中心有部分服务网关不做过滤,所以需要单独配置) 2. 网关`cn/t/notebook/gateway/config/GatewayConfig.java` 1. `corsFilter`方法中配置允许的前台请求 > cors允许的跨域配置在nacos中,dataId为cors.yaml 8. 本工程部署时将前台打包后的dist目录中所有文件放到了`file`模块的`static`目录下,以file模块作为服务器。同时,`file`模块配置了404错误页面-指向`static`下的index.html,防止刷新后找不到路由页面了。 > 最终部署时使用了gitee pages服务托管前台文件,不再使用file模块部署了 > > gitee pages提供了单页面刷新的能力,只需要在根目录新建.spa文件即可,不用输入任何内容,即可完成静态刷新并保持路由的功能 ```java @Configuration public class RefreshPageConfig implements ErrorPageRegistrar { @Override public void registerErrorPages(ErrorPageRegistry registry) { /*错误类型为404,找不到网页的,默认显示404.html网页*/ ErrorPage e404 = new ErrorPage(HttpStatus.NOT_FOUND, "/index.html"); registry.addErrorPages(e404); } } ``` > 由于vue使用的前台history路由,file模块中不会有相应的url路径映射,刷新时会被跳转到404页面,所以需要配置404显示地址,将其引导回vue的index让其路由生效。 #### docker部署说明(最终部署) **此处注意最终的后台服务器需要开放 网关端口(9000),所有的服务都是通过网关转发的** **后台服务只需要暴露网关端口9000即可,该端口使用免费的内网穿透(目前使用的是云服务器公网IP+自行搭建frp内网穿透)进行映射** **前台页面部署在云服务器上,或者可以通过另一条隧道映射** > 最终前台页面部署时使用了gitee pages服务托管前台文件 > > http://tmq777.gitee.io/notebook-web-app > > 每次更新静态文件后重新部署即可 **注意前台页面所在的ip域名需要被后台允许跨域,所有的有跨域配置的服务都需要进行配置** **Docker部署时很占用内存,如果出现部署失败,但是本地却是好的,可能是虚拟机内存不够了,需要扩展** 1. 需要部署到docker上的服务,resource下都会建立Dockerfile 2. 在idea中编辑docker配置、选择Dockerfile配置 3. 选择Dockerfile路径、指定Context folder为 projectName/target > 例如, auth服务的Context folder为 auth/target > > 因为dockerfile构建时根据相对路径构建,打包后的Dockerfile位于target下 4. 指定image tag和Container name 5. 指定Bind ports 6. 指定启动参数 --net=host > 在Run options处指定以宿主机网络映射启动,容器间可以访问 > > 否则服务起来后可能访问不到redis/mysql 7. 最后添加maven命令,编译打包 `clean package -Dmaven.test.skip=true` 8. 截图如下 ![image-20220416152254939](README.assets/image-20220416152254939.png) 9. docker插件如下 ```XML com.spotify docker-maven-plugin 1.0.0 http://localhost:12375 ${docker.image.prefix}/${project.artifactId} src/main/resource/docker / ${project.build.directory} ${project.build.finalName}.jar ``` > ${docker.image.prefix}自己在properties标签内指定 10. 运行配置好的docker配置即可远程推送到docker服务器 --- #### 系统关键点说明 1. 用户每次登录后,在保持当前系统不退出的情况下,在24小时内不会出现超时,超过24小时或者中途关闭了浏览器或当前`tab`,则需要重新登录 2. 笔记编辑器取消了图片上传的功能,原因是本系统主要用户`markdown`文档的同步和备份,如果md中有自定义的图片可能会导致不一致,当前,可公开访问的图片链接除外。 > 该功能重新打包编译了第三方依赖包,编译后的文件和前台代码在同一目录,使用时解压覆盖依赖即可 --- #### 遇到的问题 1. 使用nacos作为注册中心时(版本如上文所述), `SpringCloudGateway`可能会出现`//lb`服务转发失败报503的问题,原因如下 因为 `ReactiveLoadBalancerClientFilter` 全局过滤器没有加载 `ReactiveLoadBalancerClientFilter` 是否会加载取决于 `GatewayReactiveLoadBalancerClientAutoConfiguration` 配置类中的条件,如下 ```java @Bean @ConditionalOnBean(LoadBalancerClientFactory.class) @ConditionalOnMissingBean(ReactiveLoadBalancerClientFilter.class) @ConditionalOnEnabledGlobalFilter public ReactiveLoadBalancerClientFilter gatewayLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, GatewayLoadBalancerProperties properties, LoadBalancerProperties loadBalancerProperties) { return new ReactiveLoadBalancerClientFilter(clientFactory, properties, loadBalancerProperties); } ``` 上述源代码中类 `LoadBalancerClientFactory` 是包 `spring-cloud-loadbalancer` 的,所以如果缺少则不会注入过滤器 `ReactiveLoadBalancerClientFilter`。 引入相关依赖即可 ```xml org.springframework.cloud spring-cloud-loadbalancer ``` 2. 使用`druid`数据源时`maxWait`属性不能配置太大,否则会引起长时间等待导致服务接口不可用 3. 做一对多查询时,由于`mybatis`会在数据查询完成后进行实体映射,此时如果限制了limit分页,那么当前页一旦有重复数据后,最终实体映射数据会少于limit 数量,解决办法是先保证需要的数据不重复,然后主表再连接关联表进行查询,如下: ```sql select a.id, a.create_time, a.update_time, a.title, a.locked, a.category_name, b.note_id, b.label_name from (select s1.id, DATE_FORMAT( s1.create_time, '%Y/%m/%d %H:%i' ) AS create_time, DATE_FORMAT( s1.update_time, '%Y/%m/%d %H:%i' ) AS update_time, s1.title, s1.locked, s2.category_name from notes s1 inner join categories s2 ON s1.category_id = s2.category_id where s1.user_id = s1.user_id = #{userId, jdbcType=INTEGER} limit #{pageIndex, jdbcType=INTEGER}, 10) as a left join labels b ON a.id = b.note_id ``` > 直接查询主表然后限制分页数据,查询完的结果集作为主表再次连接关联表,最终查询出的数据可能会大于10条(limit 值),但是经过mybatis映射后最终会变成10条 4. 部署在真正服务器上时,注意从网关开始到各个服务的跨域设置都要`addAllowedOriginPattern`,添加的值为服务器前端代码所在的ip和端口 5. springboot打包后如果需要读取classpath路径下的文件,则需要改为使用流读取,否则可能会找不到文件 ```java // 注意要添加"/"前缀才会从类路径开始,否则读取的是相对路径 this.getClass().getResourceAsStream("/China-City-List-latest.csv") ``` 6. 读取yml中配置的数组需要使用对象来接收 ```java @Bean @ConfigurationProperties(prefix = "notebook.cors") public ListPropertiesContainerListPropertiesContainer // ListPropertiesContainer对象中有一个properties属性,List类型 return new ListPropertiesContainer(); } ```