搭建一个完整的微服务项目

蔚落 2023-10-01 19:48 87阅读 0赞

一.项目技术架构

1.技术栈

前台技术
Node.js、Npm、Vue.js、WebPack、Vue Cli、Element UI
后台架构
微服务架构:按照功能拆分N多个服务,每个服务可以独立技术选型,独立开发,独立部署,独立运维.,单个服务使用基于ssm的springboot,服务间通过spring cloud协调。

2.后端项目微服务原型搭建

2.1 项目基本模块搭建

  1. hrm-parent
  2. hrm-basic-parent //项目基本模块
  3. hrm-basic-utils //公共工具模块
  4. hrm-basic-common //公共代码模块
  5. hrm-support-parent //springcloud微服务支持模块
  6. hrm-eureka-server-1010
  7. hrm-gateway-zuul-1020
  8. hrm-config-server-1030
  9. hrm-system-parent
  10. hrm-systemmanage-common //针对系统管理服务公共代码如:domain,query
  11. hrm-systemmanage-service-2010 //针对于系统管理的微服务
2.1.1 hrm-parent的搭建
  1. Maven结构
  2. 先在顶层父模块进行设置管理依赖包和版本号以及一些公共的jar包。
  3. <parent>
  4. <groupId>org.springframework.boot</groupId>
  5. <artifactId>spring-boot-starter-parent</artifactId>
  6. <version>2.0.5.RELEASE</version>
  7. </parent>
  8. <properties>
  9. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  10. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  11. <java.version>1.8</java.version>
  12. <spring-cloud.version>Finchley.SR1</spring-cloud.version>
  13. </properties>
  14. <!--所有子模块一定要用到的公共的jar包-->
  15. <dependencies>
  16. <dependency>
  17. <groupId>junit</groupId>
  18. <artifactId>junit</artifactId>
  19. <version>4.12</version>
  20. </dependency>
  21. </dependencies>
  22. <dependencyManagement>
  23. <dependencies>
  24. <dependency>
  25. <!--springcloud版本管理,springcloud相关模块引入是就不需要制定版本了-->
  26. <groupId>org.springframework.cloud</groupId>
  27. <artifactId>spring-cloud-dependencies</artifactId>
  28. <version>${spring-cloud.version}</version>
  29. <type>pom</type>
  30. <scope>import</scope>
  31. </dependency>
  32. </dependencies>
  33. </dependencyManagement>
  34. SpringCloud组件之五大神兽:
  35. 服务注册发现——Netflix Eureka 帮我们服务的通信地址的
  36. 客服端负载均衡——Netflix Ribbon\Feign 解决网络通信的
  37. 断路器——Netflix Hystrix :解决微服务故障的
  38. 服务网关——Netflix Zuul :微服务的大门(安保部门)
  39. 分布式配置——Spring Cloud Config :统一管理微服务的配置
2.1.2.Eureka注册中心

Eureka是netflix的一个子模块,也是核心模块之一,Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务架构来说是非常重要的,有了服务发现和注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务,而不需要修改服务调用的配置文件了,功能类似于dubbo的注册中心,比如zookeeper。

2.1.2.1创建项目

在hrm-parent里面的hrm-support-parent进行模块化搭建注册中心
必须模块化搭建,依赖父项目
在注册中心的pom.xml导包

  1. <!--Eureka服务端支持-->
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
  5. </dependency>

配置yml

  1. server:
  2. port: 1010
  3. eureka: #Eureka的配置
  4. instance:
  5. hostname: localhost #主机
  6. client: #对Eureka客户端配置
  7. registerWithEureka: false #注册中心自己 , 不准向注册中心自己注册
  8. fetchRegistry: false #注册中心不需要 获取服务的通信地址清单
  9. serviceUrl: #注册中心 服务的注册地址
  10. #defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
  11. defaultZone: http://localhost:1010/eureka/

在配置类上写上相应注解之后main启动

  1. @SpringBootApplication
  2. @EnableEurekaClient
  3. public class EurekaServerApplication1010 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(EurekaServerApplication1010.class);
  6. }
  7. }

看到这个界面代表注册中心启动成功

2.1.3.config-server

创建网关项目
导包

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  5. </dependency>
  6. <!-- 集成Web的jar包-->
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-web</artifactId>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.springframework.cloud</groupId>
  13. <artifactId>spring-cloud-config-server</artifactId>
  14. </dependency>
  15. </dependencies>

配置yml文件

  1. eureka:
  2. client:
  3. serviceUrl:
  4. defaultZone: http://localhost:1010/eureka/ #注册中心服务端的注册地址
  5. instance:
  6. prefer-ip-address: true #使用ip进行注册
  7. instance-id: config-server:1030 #服务注册到注册中心的id
  8. server:
  9. port: 1030
  10. #应用的名字
  11. spring:
  12. application:
  13. name: config-server
  14. #码云配置
  15. cloud:
  16. config:
  17. server:
  18. git:
  19. uri: https://gitee.com/lxx/xx.git #你的仓库地址(gtihub、gtilab、码云)
  20. username: xx@qq.com #你的仓库的账户
  21. password: xxx #你账户的密码
  22. search-paths: hrm-parent/configfiles #从git 仓库的哪个目录找配置文件

配置类打上注解

  1. @SpringBootApplication
  2. @EnableConfigServer
  3. public class ConfigServerApplication1030 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(ConfigServerApplication1030.class);
  6. }
  7. }

启动之后测试
http://localhost:1030/application-zuul-dev.yml 能读取配置文件,配置中心就ok了

2.1.4.Zuul GateWay

创建项目
导包

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.cloud</groupId>
  8. <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.boot</groupId>
  12. <artifactId>spring-boot-starter-web</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>org.springframework.cloud</groupId>
  16. <artifactId>spring-cloud-starter-config</artifactId>
  17. </dependency>
  18. </dependencies>

配置application-zuul-dev.yml文件,上传你的仓库。

  1. server:
  2. port: 1020
  3. #应用的名字
  4. spring:
  5. application:
  6. name: zuul-gateway
  7. zuul:
  8. ignored-services: "*" #禁止使用服务名字进行访问
  9. prefix: "/hrm" #统一的前缀
  10. routes: #配置路由,指定服务的访问路径
  11. pay-server: "/pay/**"
  12. course-server: "/course/**"
  13. system-server: "/system/**"
  14. redis-server: "/redis/**"
  15. ribbon:
  16. ConnectTimeout: 250 # 连接超时时间(ms)
  17. ReadTimeout: 2000 # 通信超时时间(ms)
  18. OkToRetryOnAllOperations: true # 是否对所有操作重试
  19. MaxAutoRetriesNextServer: 2 # 同一服务不同实例的重试次数
  20. MaxAutoRetries: 1 # 同一实例的重试次数
  21. hystrix:
  22. command:
  23. default:
  24. execution:
  25. isolation:
  26. thread:
  27. timeoutInMillisecond: 3000 # 熔断超时时长:3000ms

配置bootstrap.yml文件

  1. spring:
  2. cloud:
  3. config:
  4. uri: http://localhost:1030
  5. name: application-zuul
  6. profile: dev #环境 组成完整的文件名

在配置类配置

  1. @SpringBootApplication
  2. @EnableZuulProxy
  3. public class ZuulServerApplication1020 {
  4. public static void main(String[] args) {
  5. SpringApplication.run(ZuulServerApplication1020.class);
  6. }
  7. }

启动之后能从你的仓库拿到你配置文件启动就ok了(如果报错,例如端口8080或者其他都是没有从你仓库拿到你的配置文件)。

2.1.5 system-2010(步骤同上,差不多)

创建项目
导包

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-web</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.cloud</groupId>
  12. <artifactId>spring-cloud-starter-config</artifactId>
  13. </dependency>
  14. </dependencies>

在application-system-dev.yml配置之后上传你的仓库

  1. eureka:
  2. client:
  3. serviceUrl:
  4. defaultZone: http://localhost:1010/eureka/ #注册中心服务端的注册地址
  5. instance:
  6. prefer-ip-address: true #使用ip进行注册
  7. instance-id: system-server:2010 #服务注册到注册中心的id
  8. server:
  9. port: 2010
  10. #应用的名字
  11. spring:
  12. application:
  13. name: system-server

配置bootstrap.yml文件

  1. spring:
  2. cloud:
  3. config:
  4. uri: http://localhost:1030
  5. name: application-system
  6. profile: dev #环境 组成完整的文件名

在配置类打上注解并启动,启动成功就OK

  1. @SpringBootApplication
  2. public class SystemServerApplication2010 {
  3. public static void main(String[] args) {
  4. SpringApplication.run(SystemServerApplication2010.class);
  5. }
  6. }

在hrm-basic-parent里面创建hrm-code-generate(代码生成)
创建项目
导包

  1. <dependencies>
  2. <dependency>
  3. <groupId>com.baomidou</groupId>
  4. <artifactId>mybatis-plus-boot-starter</artifactId>
  5. </dependency>
  6. <!--模板引擎-->
  7. <dependency>
  8. <groupId>org.apache.velocity</groupId>
  9. <artifactId>velocity-engine-core</artifactId>
  10. <version>2.0</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>mysql</groupId>
  14. <artifactId>mysql-connector-java</artifactId>
  15. </dependency>
  16. </dependencies>

创建代码生成的类

  1. package com.tys.hrm;
  2. import com.baomidou.mybatisplus.generator.AutoGenerator;
  3. import com.baomidou.mybatisplus.generator.InjectionConfig;
  4. import com.baomidou.mybatisplus.generator.config.*;
  5. import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
  6. import com.baomidou.mybatisplus.generator.config.po.TableInfo;
  7. import com.baomidou.mybatisplus.generator.config.rules.DbType;
  8. import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
  9. import java.util.*;
  10. //代码生成的主类
  11. public class GenteratorCode {
  12. //运行main方法就可以生成代码了
  13. public static void main(String[] args) throws InterruptedException {
  14. //用来获取Mybatis-Plus.properties文件的配置信息
  15. //不要加后缀
  16. ResourceBundle rb = ResourceBundle.getBundle("mybatiesplus-config-course");
  17. AutoGenerator mpg = new AutoGenerator();
  18. // 全局配置
  19. GlobalConfig gc = new GlobalConfig();
  20. gc.setOutputDir(rb.getString("OutputDir"));
  21. gc.setFileOverride(true);
  22. gc.setActiveRecord(true);// 开启 activeRecord 模式
  23. gc.setEnableCache(false);// XML 二级缓存
  24. gc.setBaseResultMap(true);// XML ResultMap
  25. gc.setBaseColumnList(false);// XML columList
  26. gc.setAuthor(rb.getString("author"));
  27. mpg.setGlobalConfig(gc);
  28. // 数据源配置
  29. DataSourceConfig dsc = new DataSourceConfig();
  30. dsc.setDbType(DbType.MYSQL);
  31. dsc.setTypeConvert(new MySqlTypeConvert());
  32. dsc.setDriverName("com.mysql.jdbc.Driver");
  33. dsc.setUsername(rb.getString("jdbc.user"));
  34. dsc.setPassword(rb.getString("jdbc.pwd"));
  35. dsc.setUrl(rb.getString("jdbc.url"));
  36. mpg.setDataSource(dsc);
  37. // 策略配置
  38. StrategyConfig strategy = new StrategyConfig();
  39. strategy.setTablePrefix(new String[] {
  40. "t_" });// 此处可以修改为您的表前缀
  41. strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略
  42. strategy.setInclude(new String[]{
  43. "t_course_type"}); // 需要生成的表 :
  44. mpg.setStrategy(strategy);
  45. // 包配置
  46. PackageConfig pc = new PackageConfig();
  47. pc.setParent(rb.getString("parent")); //基本包 cn.itsource.system
  48. pc.setController("web.controller");
  49. pc.setService("service");
  50. pc.setServiceImpl("service.impl");
  51. pc.setEntity("domain");
  52. pc.setMapper("mapper");
  53. mpg.setPackageInfo(pc);
  54. // 注入自定义配置,可以在 VM 中使用 cfg.abc 【可无】
  55. InjectionConfig cfg = new InjectionConfig() {
  56. @Override
  57. public void initMap() {
  58. Map<String, Object> map = new HashMap<String, Object>();
  59. map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-rb");
  60. this.setMap(map);
  61. }
  62. };
  63. List<FileOutConfig> focList = new ArrayList<FileOutConfig>();
  64. // 调整 controller 生成目录演示
  65. focList.add(new FileOutConfig("/templates/controller.java.vm") {
  66. @Override
  67. public String outputFile(TableInfo tableInfo) {
  68. //controller输出完整路径
  69. return rb.getString("OutputDir")+ "/com/tys/hrm/course/web/controller/" + tableInfo.getEntityName() + "Controller.java";
  70. }
  71. });
  72. // 调整 query 生成目录演示
  73. focList.add(new FileOutConfig("/templates/query.java.vm") {
  74. @Override
  75. public String outputFile(TableInfo tableInfo) {
  76. //query输出完整路径
  77. return rb.getString("OutputDirBase")+ "/com/tys/hrm/course/query/" + tableInfo.getEntityName() + "Query.java";
  78. }
  79. });
  80. // 调整 domain 生成目录演示 , 你的domain到底要输出到哪儿????,你的domain怎么输出
  81. focList.add(new FileOutConfig("/templates/entity.java.vm") {
  82. @Override
  83. public String outputFile(TableInfo tableInfo) {
  84. //domain输出完整路径
  85. return rb.getString("OutputDirBase")+ "/com/tys/hrm/course/domain/" + tableInfo.getEntityName() + ".java";
  86. }
  87. });
  88. // 调整 xml 生成目录演示
  89. focList.add(new FileOutConfig("/templates/mapper.xml.vm") {
  90. @Override
  91. public String outputFile(TableInfo tableInfo) {
  92. return rb.getString("OutputDirXml")+ "/com/tys/course/mapper/" + tableInfo.getEntityName() + "Mapper.xml";
  93. }
  94. });
  95. cfg.setFileOutConfigList(focList);
  96. mpg.setCfg(cfg);
  97. // 自定义模板配置,可以 copy 源码 mybatis-plus/src/main/resources/templates 下面内容修改,
  98. // 放置自己项目的 src/main/resources/templates 目录下, 默认名称一下可以不配置,也可以自定义模板名称
  99. TemplateConfig tc = new TemplateConfig();
  100. tc.setService("/templates/service.java.vm");
  101. tc.setServiceImpl("/templates/serviceImpl.java.vm");
  102. tc.setEntity(null);
  103. tc.setMapper("/templates/mapper.java.vm");
  104. tc.setController(null);
  105. tc.setXml(null);
  106. // 如上任何一个模块如果设置 空 OR Null 将不生成该模块。
  107. mpg.setTemplate(tc);
  108. // 执行生成
  109. mpg.execute();
  110. }
  111. }

创建mybatiesplus-config-course.properties文件

  1. #此处为本项目src所在路径(代码生成器输出路径),注意一定是当前项目所在的目录哟
  2. #mapper,servier,controller输出目录
  3. OutputDir=E:/IdeaProjects/hrm/hrm-parent/hrm-course-parent/hrm-course-service-2020/src/main/java
  4. #mapper.xml SQL映射文件目录
  5. OutputDirXml=E:/IdeaProjects/hrm/hrm-parent/hrm-course-parent/hrm-course-service-2020/src/main/resources
  6. #domain,query输出的目录
  7. OutputDirBase=E:/IdeaProjects/hrm/hrm-parent/hrm-course-parent/hrm-course-common/src/main/java
  8. #设置作者
  9. author=tys
  10. #自定义包路径
  11. parent=com.tys.hrm.course
  12. #数据库连接信息
  13. jdbc.driver=com.mysql.jdbc.Driver
  14. jdbc.url=jdbc:mysql:///hrm-course
  15. jdbc.user=root
  16. jdbc.pwd=123456

然后去system模块添加依赖包
在hrm-system-common导包

  1. <dependencies>
  2. <dependency>
  3. <groupId>com.tys</groupId>
  4. <artifactId>hrm-basic-common</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.baomidou</groupId>
  8. <artifactId>mybatis-plus-boot-starter</artifactId>
  9. <version>2.2.0</version>
  10. </dependency>
  11. </dependencies>

因为生成的mapper、service、controller需连接数据库,所以hrm-system-service-2010也要导包

  1. <dependency>
  2. <groupId>com.tys</groupId>
  3. <artifactId>hrm-system-common</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>mysql</groupId>
  7. <artifactId>mysql-connector-java</artifactId>
  8. </dependency>
  9. <dependency>
  10. <groupId>com.alibaba</groupId>
  11. <artifactId>druid</artifactId>
  12. <version>1.1.11</version>
  13. </dependency>
  14. <dependency>
  15. <groupId>com.tys</groupId>
  16. <artifactId>hrm-basic-utils</artifactId>
  17. </dependency>

让后直接点代码生成类的main方法自动生成代码,这样,domain、query、mapper、service、controller都创建完成了

2.1.6 course-server(步骤同system,差不多)

创建完成之后,用代码生成器生成course的所有。

2.2.接口文档Swagger

在创建的所有代码生成的服务(system、course)导包 和网关zuul服务也到入swagger包

  1. <!--引入swagger支持-->
  2. <dependency>
  3. <groupId>io.springfox</groupId>
  4. <artifactId>springfox-swagger2</artifactId>
  5. <version>2.9.2</version>
  6. </dependency>
  7. <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
  8. <dependency>
  9. <groupId>io.springfox</groupId>
  10. <artifactId>springfox-swagger-ui</artifactId>
  11. <version>2.9.2</version>
  12. </dependency>

在这些包里面创建swagger的类创建接口文档

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import springfox.documentation.builders.ApiInfoBuilder;
  4. import springfox.documentation.builders.PathSelectors;
  5. import springfox.documentation.builders.RequestHandlerSelectors;
  6. import springfox.documentation.service.ApiInfo;
  7. import springfox.documentation.service.Contact;
  8. import springfox.documentation.spi.DocumentationType;
  9. import springfox.documentation.spring.web.plugins.Docket;
  10. import springfox.documentation.swagger2.annotations.EnableSwagger2;
  11. @Configuration
  12. @EnableSwagger2
  13. public class Swagger2 {
  14. @Bean
  15. public Docket createRestApi() {
  16. return new Docket(DocumentationType.SWAGGER_2)
  17. .apiInfo(apiInfo())
  18. .select()
  19. //对外暴露服务的包,以controller的方式暴露,所以就是controller的包.
  20. .apis(RequestHandlerSelectors.basePackage("com.tys.hrm.course.web.controller"))
  21. .paths(PathSelectors.any())
  22. .build();
  23. }
  24. private ApiInfo apiInfo() {
  25. return new ApiInfoBuilder()
  26. .title("平台服务api")
  27. .description("平台服务接口文档说明")
  28. .contact(new Contact("yhptest", "", "yhp@itsource.cn"))
  29. .version("1.0")
  30. .build();
  31. }
  32. }

然后重新启动,访问http://localhost:2020/swagger-ui.html、http://localhost:2010/swagger-ui.html
在zuul创建一个配置config包,创建swagger类

  1. package com.tys.hrm.config;
  2. import org.springframework.context.annotation.Primary;
  3. import org.springframework.stereotype.Component;
  4. import springfox.documentation.swagger.web.SwaggerResource;
  5. import springfox.documentation.swagger.web.SwaggerResourcesProvider;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. @Component
  9. @Primary
  10. public class DocumentationConfig implements SwaggerResourcesProvider {
  11. @Override
  12. public List<SwaggerResource> get() {
  13. List resources = new ArrayList<>();
  14. resources.add(swaggerResource("系统管理", "/hrm/system/v2/api-docs", "2.0"));
  15. resources.add(swaggerResource("课程管理", "/hrm/course/v2/api-docs", "2.0"));
  16. return resources;
  17. }
  18. private SwaggerResource swaggerResource(String name, String location, String version) {
  19. SwaggerResource swaggerResource = new SwaggerResource();
  20. swaggerResource.setName(name);
  21. swaggerResource.setLocation(location);
  22. swaggerResource.setSwaggerVersion(version);
  23. return swaggerResource;
  24. }
  25. }
  26. package com.tys.hrm.config;
  27. import org.springframework.context.annotation.Bean;
  28. import org.springframework.context.annotation.Configuration;
  29. import springfox.documentation.builders.ApiInfoBuilder;
  30. import springfox.documentation.service.ApiInfo;
  31. import springfox.documentation.service.Contact;
  32. import springfox.documentation.spi.DocumentationType;
  33. import springfox.documentation.spring.web.plugins.Docket;
  34. import springfox.documentation.swagger2.annotations.EnableSwagger2;
  35. @Configuration
  36. @EnableSwagger2
  37. public class SwaggerConfig {
  38. @Bean
  39. public Docket createRestApi() {
  40. return new Docket(DocumentationType.SWAGGER_2)
  41. .apiInfo(apiInfo());
  42. }
  43. private ApiInfo apiInfo() {
  44. return new ApiInfoBuilder()
  45. .title("资源管理系统")
  46. .description("资源管理系统接口文档说明")
  47. .termsOfServiceUrl("http://localhost:1020")
  48. .contact(new Contact("yphtest", "", "yhp@itsoruce.cn"))
  49. .version("1.0")
  50. .build();
  51. }
  52. }

然后重启zuul服务,访问http://localhost:1020/swagger-ui.html

2.3.elementui+vue

前端启动 npm run dev
因为前后端分离,访问后台会出现跨域问题,跨越配置-在zuul进行配置(所有前端统一入口)。

  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.cors.CorsConfiguration;
  4. import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
  5. import org.springframework.web.filter.CorsFilter;
  6. @Configuration
  7. public class GlobalCorsConfig {
  8. @Bean
  9. public CorsFilter corsFilter() {
  10. //1.添加CORS配置信息
  11. CorsConfiguration config = new CorsConfiguration();
  12. //1) 允许的域,不要写*,否则cookie就无法使用了
  13. config.addAllowedOrigin("http://127.0.0.1:6001");
  14. config.addAllowedOrigin("http://localhost:6001");
  15. //2) 是否发送Cookie信息
  16. config.setAllowCredentials(true);
  17. //3) 允许的请求方式
  18. config.addAllowedMethod("OPTIONS");
  19. config.addAllowedMethod("HEAD");
  20. config.addAllowedMethod("GET");
  21. config.addAllowedMethod("PUT");
  22. config.addAllowedMethod("POST");
  23. config.addAllowedMethod("DELETE");
  24. config.addAllowedMethod("PATCH");
  25. // 4)允许的头信息
  26. config.addAllowedHeader("*");
  27. //2.添加映射路径,我们拦截一切请求
  28. UrlBasedCorsConfigurationSource configSource = new
  29. UrlBasedCorsConfigurationSource();
  30. configSource.registerCorsConfiguration("/**", config);
  31. //3.返回新的CorsFilter.
  32. return new CorsFilter(configSource);
  33. }
  34. }

配置之后重启zuul服务。刷新前端就能访问了。

2.4.redis+feign

在这里插入图片描述

2.4.1.搭建项目结构
  1. hrm-redis-parent
  2. hrm-redis-client
  3. hrm-redis-service-2030
2.4.1.1 redis

搭建 hrm-redis-service-2030 导入依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-web</artifactId>
  9. </dependency>
  10. <dependency>
  11. <groupId>org.springframework.cloud</groupId>
  12. <artifactId>spring-cloud-starter-config</artifactId>
  13. </dependency>
  14. <dependency>
  15. <groupId>redis.clients</groupId>
  16. <artifactId>jedis</artifactId>
  17. </dependency>
  18. <dependency>
  19. <groupId>com.tys</groupId>
  20. <artifactId>hrm-basic-utils</artifactId>
  21. </dependency>
  22. </dependencies>

准备Redis工具类

  1. 配置文件 redis.properties
  2. redis.host=127.0.0.1
  3. redis.port=6379
  4. redis.password=123456
  5. redis.timeout=5000

RedisUtil

  1. import redis.clients.jedis.Jedis;
  2. import redis.clients.jedis.JedisPool;
  3. import redis.clients.jedis.JedisPoolConfig;
  4. import java.io.IOException;
  5. import java.util.Properties;
  6. /**
  7. * 获取连接池对象
  8. */
  9. public enum RedisUtils {
  10. INSTANCE;
  11. static JedisPool jedisPool = null;
  12. static {
  13. //1 创建连接池配置对象
  14. JedisPoolConfig config = new JedisPoolConfig();
  15. //2 进行配置-四个配置
  16. config.setMaxIdle(1);//最小连接数
  17. config.setMaxTotal(11);//最大连接数
  18. config.setMaxWaitMillis(10 * 1000L);//最长等待时间
  19. config.setTestOnBorrow(true);//测试连接时是否畅通
  20. //3 通过配置对象创建连接池对象
  21. Properties properties = null;
  22. try {
  23. properties = new Properties(); properties.load(RedisUtils.class.getClassLoader().getResourceAsStream("redis.properties"));
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. String host = properties.getProperty("redis.host");
  28. String port = properties.getProperty("redis.port");
  29. String password = properties.getProperty("redis.password");
  30. String timeout = properties.getProperty("redis.timeout");
  31. jedisPool = new JedisPool(config, host, Integer.valueOf(port),Integer.valueOf(timeout), password);
  32. }
  33. //获取连接
  34. public Jedis getSource() {
  35. return jedisPool.getResource();
  36. }
  37. //关闭资源
  38. public void closeSource(Jedis jedis) {
  39. if (jedis != null) {
  40. jedis.close();
  41. }
  42. }
  43. /**
  44. * 设置字符值
  45. *
  46. * @param key
  47. * @param value
  48. */
  49. public void set(String key, String value) {
  50. Jedis jedis = getSource();
  51. jedis.set(key, value);
  52. closeSource(jedis);
  53. }
  54. /**
  55. * 设置
  56. * @param key
  57. * @param value
  58. */
  59. public void set(byte[] key, byte[] value) {
  60. Jedis jedis = getSource();
  61. jedis.set(key, value);
  62. closeSource(jedis);
  63. }
  64. /**
  65. *
  66. * @param key
  67. * @return
  68. */
  69. public byte[] get(byte[] key) {
  70. Jedis jedis = getSource();
  71. try {
  72. return jedis.get(key);
  73. } catch (Exception e) {
  74. e.printStackTrace();
  75. } finally {
  76. closeSource(jedis);
  77. }
  78. return null;
  79. }
  80. /**
  81. * 设置字符值
  82. *
  83. * @param key
  84. */
  85. public String get(String key) {
  86. Jedis jedis = getSource();
  87. try {
  88. return jedis.get(key);
  89. } catch (Exception e) {
  90. e.printStackTrace();
  91. } finally {
  92. closeSource(jedis);
  93. }
  94. return null;
  95. }
  96. }

编写RedisController

  1. /**
  2. * redis的接口
  3. */
  4. @RestController
  5. @RequestMapping("/redis")
  6. public class RedisController {
  7. @GetMapping("/get/{key}")
  8. public AjaxResult get(@PathVariable("key")String key){
  9. String result = RedisUtils.INSTANCE.get(key);
  10. return AjaxResult.me().setResultObj(result);
  11. }
  12. @PostMapping("/set")
  13. public AjaxResult set(@RequestParam("key")String key,@RequestParam("value")String value){
  14. RedisUtils.INSTANCE.set(key,value);
  15. return AjaxResult.me();
  16. }
  17. @PostMapping("/setex")
  18. public AjaxResult setex(@RequestParam("key")String key,
  19. @RequestParam("value")String value,
  20. @RequestParam("seconds")int seconds){
  21. RedisUtils.INSTANCE.setex(key,value,seconds);
  22. return AjaxResult.me();
  23. }
  24. }

配置application-redis-dev.yml,配置成功上传仓库

  1. eureka:
  2. client:
  3. serviceUrl:
  4. defaultZone: http://localhost:1010/eureka/ #注册中心地址
  5. instance:
  6. prefer-ip-address: true #使用ip地址注册
  7. instance-id: redis-service #指定服务的id
  8. server:
  9. port: 2030
  10. spring:
  11. application:
  12. name: redis-service

再配合bootstrap.yml

  1. spring:
  2. cloud:
  3. config:
  4. uri: http://localhost:1030
  5. name: application-redis
  6. profile: dev #环境 组成完整的文件名
2.4.1.2 feign

hrm-redis-feign导包

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-openfeign</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.tys</groupId>
  8. <artifactId>hrm-basic-utils</artifactId>
  9. </dependency>
  10. </dependencies>

再里面写一个feign接口

  1. //value属性:调用目标服务的服务名
  2. @FeignClient(value = "redis-server")
  3. public interface RedisFeignClient {
  4. //设置值
  5. @PostMapping("/redis/set")
  6. AjaxResult set(@RequestParam("key")String key, @RequestParam("value")String value);
  7. @GetMapping("/redis/get/{key}")
  8. AjaxResult get(@PathVariable("key")String key);
  9. }

在需要缓存的地方依赖hrm-redis-feign项目
在这个微服务类开启配置

  1. @SpringBootApplication
  2. @MapperScan("com.tys.hrm.course.mapper")
  3. @EnableTransactionManagement
  4. @EnableFeignClients("com.tys.hrm.feignclients")
  5. public class CourseServerApplication2020 {
  6. public static void main(String[] args) {
  7. SpringApplication.run(CourseServerApplication2020.class);
  8. }
  9. /**
  10. * 分页插件
  11. */
  12. @Bean
  13. public PaginationInterceptor paginationInterceptor() {
  14. return new PaginationInterceptor();
  15. }
  16. }

修改这个服务的service.iml的增删改查方法,添加缓存

  1. package com.tys.hrm.course.service.impl;
  2. import com.alibaba.fastjson.JSON;
  3. import com.baomidou.mybatisplus.service.impl.ServiceImpl;
  4. import com.tys.hrm.constants.RedisKeyConstants;
  5. import com.tys.hrm.course.domain.CourseType;
  6. import com.tys.hrm.course.mapper.CourseTypeMapper;
  7. import com.tys.hrm.course.service.ICourseTypeService;
  8. import com.tys.hrm.feignclients.RedisFeignClient;
  9. import com.tys.hrm.util.AjaxResult;
  10. import org.springframework.beans.factory.annotation.Autowired;
  11. import org.springframework.stereotype.Service;
  12. import java.io.Serializable;
  13. import java.util.ArrayList;
  14. import java.util.List;
  15. /**
  16. * <p>
  17. * 课程目录 服务实现类
  18. * </p>
  19. */
  20. @Service
  21. public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {
  22. @Autowired
  23. private RedisFeignClient redisFeignClient;
  24. //重置Redis中的课程分类
  25. private List<CourseType> resetRedisForCourseType(){
  26. // 如果Reids没有就从Mysql中查
  27. List<CourseType> courseTypes = baseMapper.selectList(null);
  28. // Mysql查到之后同步一份到Redis
  29. redisFeignClient.set(RedisKeyConstants.COURSE_TYPE, JSON.toJSONString(courseTypes));
  30. return courseTypes;
  31. }
  32. @Override
  33. public boolean insert(CourseType entity) {
  34. boolean insertSucess = super.insert(entity);
  35. resetRedisForCourseType();
  36. return insertSucess;
  37. }
  38. @Override
  39. public boolean deleteById(Serializable id) {
  40. boolean deleteSucess = super.deleteById(id);
  41. resetRedisForCourseType();
  42. return deleteSucess;
  43. }
  44. @Override
  45. public boolean updateById(CourseType entity) {
  46. boolean updateSuccess = super.updateById(entity);
  47. resetRedisForCourseType();
  48. return updateSuccess;
  49. }
  50. @Override
  51. public List<CourseType> treeData() {
  52. List<CourseType> courseTypes = null;
  53. // 查询课程分类的时候先查询Redis
  54. AjaxResult ajaxResult = redisFeignClient.get(RedisKeyConstants.COURSE_TYPE);
  55. //判断是否有结果
  56. if(ajaxResult.isSuccess() && null != ajaxResult.getResultObj()){
  57. //Redis中有数据
  58. //如果Redis有就直接返回、
  59. String jsonFromRedis = ajaxResult.getResultObj().toString();
  60. //存在redis中的数据 ,要不要有层级结构 :放没有处理过的list
  61. courseTypes = JSON.parseArray(jsonFromRedis , CourseType.class);
  62. }else{
  63. courseTypes = resetRedisForCourseType();
  64. }
  65. //1.查询所有的课程类型
  66. //List<CourseType> courseTypes = baseMapper.selectList(null);
  67. //2.先过滤出一级分类
  68. //用来封装一级分类,当然每个一级分类的children中有其子分类
  69. List<CourseType> primaryCourseType = new ArrayList<>();
  70. for(CourseType courseType : courseTypes){
  71. //如果pid==0,那么就是一级分类
  72. if(courseType.getPid().longValue() == 0){
  73. primaryCourseType.add(courseType);//1037
  74. }else{
  75. //2.如果不是一级分类,就要知道自己的父分类,装到自己的父分类的 children
  76. //courseType :当前分类,根据当前分类的pid 就是父分类的id
  77. CourseType currentPrimaryCourseType = null; //1037
  78. for(CourseType pcourseType : courseTypes ){
  79. if(courseType.getPid().longValue() == pcourseType.getId().longValue()){
  80. //如果当前分类(courseType)的pid 和某个分类的id相等,那么这个某个分类就是当前分类的父分类
  81. currentPrimaryCourseType = pcourseType;
  82. break;
  83. }
  84. }
  85. if(currentPrimaryCourseType != null){
  86. //3.如果找到了父分类,就把当前分类加入父分类的children中
  87. currentPrimaryCourseType.getChildren().add(courseType);
  88. }
  89. }
  90. }
  91. return primaryCourseType;
  92. }
  93. }

重启这个微服务和redis服务,开启redis

  1. redis-server.exe redis.windows.conf

然后去测试,第一次进数据库查询,并缓存到redis中,第二次查询则直接进缓存,其他操作 增删改 操作之后,进行更新缓存。

在feign接口打上注解,调用托底类

  1. @FeignClient(value = "redis-server",fallbackFactory = RedisFeignFallbackFactory.class)

在feign接口实现方法 重写方法

  1. package com.tys.hrm.fallback;
  2. import com.tys.hrm.feignclients.RedisFeignClient;
  3. import com.tys.hrm.util.AjaxResult;
  4. import feign.hystrix.FallbackFactory;
  5. import org.springframework.stereotype.Component;
  6. @Component
  7. public class RedisFeignFallbackFactory implements FallbackFactory<RedisFeignClient> {
  8. @Override
  9. public RedisFeignClient create(Throwable throwable) {
  10. return new RedisFeignClient() {
  11. //托底方法
  12. @Override
  13. public AjaxResult set(String key, String value) {
  14. throwable.printStackTrace();
  15. return AjaxResult.me().setSuccess(false).setMessage("Redis服务不可用["+throwable.getMessage()+"]");
  16. }
  17. @Override
  18. public AjaxResult get(String key) {
  19. throwable.printStackTrace();
  20. return AjaxResult.me().setSuccess(false).setMessage("Redis服务不可用["+throwable.getMessage()+"]");
  21. }
  22. };
  23. }
  24. }
2.4.1.3 fastdfs

导包

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.cloud</groupId>
  4. <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  5. </dependency>
  6. <!-- 集成Web的jar包-->
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-web</artifactId>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.springframework.cloud</groupId>
  13. <artifactId>spring-cloud-starter-config</artifactId>
  14. </dependency>
  15. <dependency>
  16. <groupId>com.tys</groupId>
  17. <artifactId>hrm-basic-utils</artifactId>
  18. </dependency>
  19. <!--引入swagger支持-->
  20. <dependency>
  21. <groupId>io.springfox</groupId>
  22. <artifactId>springfox-swagger2</artifactId>
  23. </dependency>
  24. <dependency>
  25. <groupId>io.springfox</groupId>
  26. <artifactId>springfox-swagger-ui</artifactId>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.csource</groupId>
  30. <artifactId>fastdfs-client-java</artifactId>
  31. <exclusions>
  32. <exclusion>
  33. <groupId>org.slf4j</groupId>
  34. <artifactId>slf4j-log4j12</artifactId>
  35. </exclusion>
  36. </exclusions>
  37. </dependency>
  38. <dependency>
  39. <groupId>commons-io</groupId>
  40. <artifactId>commons-io</artifactId>
  41. </dependency>
  42. </dependencies>

导入工具类fastdfs

  1. package com.tys.hrm.utils;
  2. import org.csource.common.NameValuePair;
  3. import org.csource.fastdfs.*;
  4. public class FastDfsApiOpr {
  5. public static String CONF_FILENAME = FastDfsApiOpr.class.getClassLoader()
  6. .getResource("fdfs_client.conf").getFile();
  7. /**
  8. * 上传文件
  9. * @param file
  10. * @param extName
  11. * @return
  12. */
  13. public static String upload(byte[] file,String extName) {
  14. try {
  15. ClientGlobal.init(CONF_FILENAME);
  16. TrackerClient tracker = new TrackerClient();
  17. TrackerServer trackerServer = tracker.getTrackerServer();
  18. //TrackerServer trackerServer = tracker.getConnection();
  19. StorageServer storageServer = null;
  20. StorageClient storageClient = new StorageClient(trackerServer, storageServer);
  21. NameValuePair nvp [] = new NameValuePair[]{
  22. new NameValuePair("age", "18"),
  23. new NameValuePair("sex", "male")
  24. };
  25. String fileIds[] = storageClient.upload_file(file,extName,nvp);
  26. System.out.println(fileIds.length);
  27. System.out.println("组名:" + fileIds[0]);
  28. System.out.println("路径: " + fileIds[1]);
  29. return "/"+fileIds[0]+"/"+fileIds[1];
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. return null;
  33. }
  34. }
  35. /**
  36. * 上传文件
  37. * @param extName
  38. * @return
  39. */
  40. public static String upload(String path,String extName) {
  41. try {
  42. ClientGlobal.init(CONF_FILENAME);
  43. TrackerClient tracker = new TrackerClient();
  44. TrackerServer trackerServer = tracker.getTrackerServer();
  45. //TrackerServer trackerServer = tracker.getConnection();
  46. StorageServer storageServer = null;
  47. StorageClient storageClient = new StorageClient(trackerServer, storageServer);
  48. String fileIds[] = storageClient.upload_file(path, extName,null);
  49. System.out.println(fileIds.length);
  50. System.out.println("组名:" + fileIds[0]);
  51. System.out.println("路径: " + fileIds[1]);
  52. return "/"+fileIds[0]+"/"+fileIds[1];
  53. } catch (Exception e) {
  54. e.printStackTrace();
  55. return null;
  56. }
  57. }
  58. /**
  59. * 下载文件
  60. * @param groupName
  61. * @param fileName
  62. * @return
  63. */
  64. public static byte[] download(String groupName,String fileName) {
  65. try {
  66. ClientGlobal.init(CONF_FILENAME);
  67. TrackerClient tracker = new TrackerClient();
  68. TrackerServer trackerServer = tracker.getTrackerServer();
  69. //TrackerServer trackerServer = tracker.getConnection();
  70. StorageServer storageServer = null;
  71. StorageClient storageClient = new StorageClient(trackerServer, storageServer);
  72. byte[] b = storageClient.download_file(groupName, fileName);
  73. return b;
  74. } catch (Exception e) {
  75. e.printStackTrace();
  76. return null;
  77. }
  78. }
  79. /**
  80. * 删除文件
  81. * @param groupName
  82. * @param fileName
  83. */
  84. public static void delete(String groupName,String fileName){
  85. try {
  86. ClientGlobal.init(CONF_FILENAME);
  87. TrackerClient tracker = new TrackerClient();
  88. TrackerServer trackerServer = tracker.getTrackerServer();
  89. //TrackerServer trackerServer = tracker.getConnection();
  90. StorageServer storageServer = null;
  91. StorageClient storageClient = new StorageClient(trackerServer,
  92. storageServer);
  93. int i = storageClient.delete_file(groupName,fileName);
  94. System.out.println( i==0 ? "删除成功" : "删除失败:"+i);
  95. } catch (Exception e) {
  96. e.printStackTrace();
  97. throw new RuntimeException("删除异常,"+e.getMessage());
  98. }
  99. }
  100. }

直接复制swagger
创建web.controller层

  1. package com.tys.hrm.web.controller;
  2. import com.tys.hrm.util.AjaxResult;
  3. import com.tys.hrm.utils.FastDfsApiOpr;
  4. import org.apache.commons.io.FilenameUtils;
  5. import org.springframework.web.bind.annotation.*;
  6. import org.springframework.web.multipart.MultipartFile;
  7. import java.io.IOException;
  8. //文件统一处理
  9. @RestController
  10. @RequestMapping("/fastdfs")
  11. public class FastDfsController {
  12. //文件上传
  13. @PostMapping("/upload")
  14. public AjaxResult upload(MultipartFile file) throws Exception {
  15. //把文件上传到Fastdfs云服务器
  16. try {
  17. //原生的文件名:a.jpg :commons-io
  18. String extension = FilenameUtils.getExtension(file.getOriginalFilename());
  19. String filePath = FastDfsApiOpr.upload(file.getBytes() , extension);
  20. return AjaxResult.me().setResultObj(filePath);
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. return AjaxResult.me().setSuccess(false).setMessage("文件上传失败");
  24. }
  25. }
  26. @DeleteMapping("/remove")
  27. public AjaxResult delete(@RequestParam("path") String path) throws Exception{
  28. try{
  29. /*把组名前面的/去掉
  30. * substring(int beginIndex) 返回字符串的子字符串。
  31. * substring(int beginIndex, int endIndex) beginIndex起始索引(包含)索引从0开始。endIndex结束索引(不包括).
  32. * indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
  33. * */
  34. String pathTmp = path.substring(1);
  35. //得到groupName
  36. String groupName = pathTmp.substring(0, pathTmp.indexOf("/"));
  37. //得到fileName
  38. String fileName = pathTmp.substring(pathTmp.indexOf("/")+1);
  39. System.out.println(groupName);
  40. System.out.println(fileName);
  41. FastDfsApiOpr.delete(groupName, fileName);
  42. return AjaxResult.me();
  43. }catch (Exception e){
  44. e.printStackTrace();
  45. return AjaxResult.me().setSuccess(false).setResultObj("删除失败!" + e.getMessage());
  46. }
  47. }
  48. }

设置fastDFS配置文件(fdfs_client.conf)

  1. connect_timeout = 2
  2. network_timeout = 30
  3. charset = UTF-8
  4. http.tracker_http_port = 80
  5. http.anti_steal_token = no
  6. http.secret_key = FastDFS1234567890
  7. tracker_server=118.25.154.214:22122 #服务器配置了fastDFS的IP
  8. connection_pool.enabled = true
  9. connection_pool.max_count_per_entry = 500
  10. connection_pool.max_idle_time = 3600
  11. connection_pool.max_wait_time_in_ms = 1000

发表评论

表情:
评论列表 (有 0 条评论,87人围观)

还没有评论,来说两句吧...

相关阅读

    相关 如何快速一个服务架构?

    微服务火了很久,但网上很少有文章能做到成熟地将技术传播出来,同时完美地照顾“初入微服务领域人员”,从 0 开始,采用通俗易懂的语言去讲解微服务架构的系列。所以,我们策划了这篇文

    相关 springcloud服务-项目

    前言:      乐优商城这个视频还可以,于是拿来练练手,我对着视频搭环境一直在service服务模块卡住了,注册中心和网关可以启动,服务模块却一直启动不了,报各种奇怪的错,