一个精简的微服务项目总结

一个提供SDK接口的精简微服务项目的总结,使用的是SSM(springboot + springMVC + Mybatis)框架,配合Nacos进行服务发现和注册以及服务配置,项目主要包括两个服务:一个网关服务(server_gateway)和一个接口服务(server_appsdk)。

一、IDEA环境相关

  1. Maven Helper 插件安装
    操作maven 命令,如使用maven 打包等

  2. Lombok 插件安装
    lombok 能够减少大量的模板代码,使用
    @Data 注解时可以不用书写
    getter,setter方法,
    toString方法
    hashCode方法
    equals方法等
    @NoArgsConstructor, @AllArgsConstructor 可以不用写构造函数
    需要添加依赖


    org.projectlombok
    lombok
    true

  1. MyBatisCodeHelperPro 插件安装
    设计完表后可以使用该插件自动生成dao层的表对应实体domain、mapper、以及mapper对应sql的xml文件,
    也可以生成service这个看个人需要,前面生成的dao层非常好用

  2. 如果IDEA底部找不到service面板,解决方法:
    打开.idea目录里面的 workspace.xml文件,找到 RunDashboard 项,用下面内容替换下


    
    
  

二、项目结构

1.多模块项目结构

项目名为 server-starter,是一个 新建项目 -》选择 Maven 创建的一个 maven 项目目录作为整个项目的workspace和父pom
一个多模块项目通过一个父POM 引用一个或多个子模块来定义。在父项目的pom.xml中通过以下配置,将子项目关联。

    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.7.RELEASE
        
    

    com.hp
    server-starter
    1.0.0
    pom  
    
      
         server_common   
         server_gateway  
         server_appsdk  
      

其中 pom 这个父项目不是创建一个JAR 或者一个WAR,它仅仅是一个引用其它 Maven 项目的POM。
pom.xml 中 modules 列出了项目的子模块。每个modules 元素对应了一个 server-starter 目录下的子目录。Maven知道去这些
子目录寻找pom.xml 文件,并且在构建的 server-starter 的时候,它会将这些子模块包含到要构建的项目中。
子项目的创建:在父项目目录右击new-》Module-》Spring Initalizer 创建相应的子项目

仅仅在父项目配置子项目是不能够真正实现关联的,子项目中需要配置

  
    com.hp  
    server-starter  
    1.0.0  
  

2.编译打包

现在,通过父pom.xml将多个子项目进行了关联,在server-starter上面右键 Run Maven -> clean/package 或者 clean install。
如果右键没有 Run Maven 选项,检查Maven Helper 插件是否安装好
将多个子项目打包,每个子项目都是一个独立的springboot服务都可以打成 JAR包直接在Linux 上面使用 java 命令运行

编译打包,把resources下面面的资源全部打包,并且打包到指定的目录路径下,在各个子项目的pom.xml中添加


    hp-${project.artifactId}
    
        
            src/main/resources
            true
            
                **/*.*
            
        
    
    
        
            org.springframework.boot
            spring-boot-maven-plugin
        
        
        
            maven-antrun-plugin
            
                
                    copy-jar
                    package
                    
                        
                            
                                
                                    
                                
                            
                        
                    
                    
                        run
                    
                
            
        
    

3. 服务模块代码结构

大致的结构jave目录下
com.ali.hp包下面创建以下几个包

  • config
    配置包,如 mybatis,redis,swagger 等相关配置类放在该包下

  • pojo
    自定义的各个对象数据包

  • dao
    数据库相关的数据层包,里面有 domain, mapper, mapperxml等目录,这个包可以放在 server_common 公共模块中,这一各个服务都可以使用

  • service
    提供服务包,主要的业务逻辑的处理都在该包下面,一般用接口 + 实现的层次结构,即在包下面的各个具体服务目录下 建一个接口和一个 impl 包,impl包下写接口的实现

  • controller
    提供前端访问的接口包

  • schedule
    提供定时任务的包

  • 以及其他一些需要的包

4. 多环境配置

比如配置三个环境:dev 开发环境,test 测试环境, prod 生成环境
在 resources 目录下创建4个配置文件
application.yml,application-dev.yml,application-test.yml,application-prod.yml
具体使用哪个配置文件是根据 application.yml 里面的 spring.profiles.active 值决定的。
这样在开发的时候 application-dev.yml 不需要提交到svn,其他的配置可以提交到svn
举例:
application.yml

server:
  port: 8001
spring:
  profiles:
    active: test
  application:
    name: mser-service

application-dev.yml

env:
  serv-addr: 192.168.121.223
spring:
  cloud:
    nacos:
      discovery:
        server-addr: ${env.serv-addr}:8848

application-test.yml

env:
  serv-addr: 47.96.16.xxx
spring:
  cloud:
    nacos:
      discovery:
        server-addr: ${env.serv-addr}:8848

5. 常用注解

@Data
自动生成对象属性的get,set等方法
@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor
注解在类上, 为类提供无参,有指定必须参数, 全参构造函数
@PostConstruct
注解在方法上会在服务器加载Servlet的时候运行,并且只会被服务器调用一次,类似于Serclet的inti()方法。被@PostConstruct修饰的方法会在构造函数之后,init()方法之前运行。
@PreConstruct
注解在方法上会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreConstruct修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前
@Autowired
默认按照类型装配的,属于spring的
@Resource
默认按照名字装配的,属于J2EE的
@Controller
@ResponseBody
@RestController
相当于 @Controller 和 @ResponseBody的组合, 用于 controller 层
@RequestMapping("/api/user/")
@Value("${server.port}")
读取yml里面的配置值

三、Mybatis

封装数据库相关的操作,配合MyBatisCodeHelperPro 插件非常好用
如果是多表联合查询,可以单独写个Mapper,根据需要自定义一个对象用来返回sql查询的结果

1.添加依赖



    org.springframework.boot
    spring-boot-starter-jdbc


    mysql
    mysql-connector-java
    runtime


    org.mybatis
    mybatis
    3.4.4


    org.mybatis
    mybatis-spring
    1.3.1



    com.github.pagehelper
    pagehelper
    4.2.1

2.添加配置项

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${env.serv-addr}:3306/gpsdk?characterEncoding=UTF-8&useTimezone=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    hikari:
      minimum-idle: 5 #最小连接数
      idle-timeout: 600000 #超时时间
      maximum-pool-size: 10 #连接池大小
      auto-commit: true #是否自动提交
      pool-name: MyHikariCP #连接池名字
      max-lifetime: 1800000
      connection-timeout: 30000
      connection-test-query: SELECT 1

3.创建配置类

创建一个config包,在包下建一个 MybatisConfig 配置类(在各自的服务模块上创建,不能在公共模块上建,依赖可以放到公共模块上)
在配置类中配置好MyBatisCodeHelperPro 插件自动生成dao层的domain、mapper、以及mapper对应sql的xml文件

@Configuration
@EnableTransactionManagement
@MapperScan("com.hp.common.dao.mapper")
public class MybatisConfig implements TransactionManagementConfigurer {

    @Autowired
    private DataSource dataSource;

    public MybatisConfig() {
    }

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory() {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setTypeAliasesPackage("com.hp.common.dao.domain");

        //分页插件
        PageHelper pageHelper = new PageHelper();
        Properties properties = new Properties();
        properties.setProperty("reasonable", "false");
        properties.setProperty("supportMethodsArguments", "true");
        properties.setProperty("returnPageInfo", "check");
        properties.setProperty("params", "count=countSql");
        pageHelper.setProperties(properties);
        bean.setPlugins(new Interceptor[]{pageHelper});

        try {
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            // mybatis配置
            org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
            configuration.setLazyLoadingEnabled(true);
            configuration.setUseGeneratedKeys(true);
            configuration.setDefaultExecutorType(ExecutorType.REUSE);
            configuration.setDefaultStatementTimeout(30);
            configuration.setMapUnderscoreToCamelCase(true);
            configuration.setAutoMappingBehavior(AutoMappingBehavior.FULL);
            bean.setConfiguration(configuration);

            bean.setMapperLocations(resolver.getResources("classpath*:mapper/*.xml"));
            return bean.getObject();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

四、Redis

1.添加依赖


    org.springframework.boot
    spring-boot-starter-data-redis

2.添加配置

spring:
  datasource:
    redis:
        database: 0
        host: ${env.serv-addr}
        port: 6379
        password: 
        jedis:
          pool:
            max-active: 20
            min-idle: 0
            max-idle: 20
            max-wait: -1ms

3.使用注意

redis模板对象注入 不要使用 @Autowired 会报错,应该使用 @Resource
StringRedisTemplate 可以使用@Autowired注入

    @Resource
    RedisTemplate redisTemplate;

五、Swagger

生成api接口文档,再也不用单独在写一个接口文档了,直接通过注解的方式生成文档

1.添加依赖



    io.springfox
    springfox-swagger2
    2.8.0


    io.springfox
    springfox-swagger-ui
    2.8.0

2.创建配置类

在配置类中配置好controller包的全路径名

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Value("${spring.profiles.active:dev}")
    private String env;
    @Value("${server.port}")
    private String serverPort;

    @Bean
    public Docket createAppRestApi() {
        List pars = new ArrayList<>();

        return new Docket(DocumentationType.SWAGGER_2)
                .enable("dev".equals(env) || "test".equals(env))
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.hp.mser.controller"))
                .paths(PathSelectors.any())
                .build()
                .globalOperationParameters(pars)
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("开放平台服务文档")
                .description("提供开放平台服务")
                .termsOfServiceUrl("http://localhost:" + serverPort + "/v2/api-docs")
                .contact(new Contact("x", "", ""))
                .version("1.0.0")
                .build();
    }
}

3.使用

1.数据对象
在数据对象类上添加注解
@ApiModel(value = "xxxDTO", description = "xxx请求/应答")
在对象属性字段上添加注解
@ApiModelProperty(value = "小游戏ID", name = "id")
2.访问接口
在controller里面的类上添加注解
@Api(tags = "用户接口")
在访问接口类的方法上添加注解
@ApiOperation(value = "swagger测试")

六、Nacos

Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您实现动态服务发现、服务配置管理、服务及流量管理。

1. 下载安装

前往 https://github.com/alibaba/nacos/releases 下载

tar -xvf nacos-server-1.x.x.tar.gz

启动:解压后在 nacos/bin目录下 执行如下命令启动

Linux/Unix/Mac 下

sh startup.sh -m standalone

Windows 下

cmd startup.cmd

测试:打开 http://localhost:8848/nacos/#/login ,默认账号密码都是nacos

2. 添加依赖包

    
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-config
        

3. 增加配置项

spring:
  cloud:
    nacos:
      discovery:
        server-addr: ${env.address}:8848

4. 添加注解

在程序的main方法上添加:
@EnableDiscoveryClient 开启服务注册发现功能

5. 导入配置

server-appsdk.jar 运行启动时优先会读取同级目录下的启动配置文件,然后根据该配置文件找到nacos上配置的程序需要的配置文件

bootstrap.properties

# nacos配置服务地址
spring.cloud.nacos.config.server-addr=localhost:8848
# 配置文件名
spring.cloud.nacos.config.prefix=server-appsdk
# 环境
spring.profiles.active=test
# 后缀
spring.cloud.nacos.config.file-extension=yaml
#最终生成的文件名规则
# ${spring.cloud.nacos.config.prefix}-${spring.profile.active}.${file-extension}

在 nacos 控制台中 配置管理 | 配置列表 | 导入配置 上传 server-appsdk-test.yaml 配置文件,这样jar包程序就可以读取到这个配置了。

6. 服务管理

程序启动后,在 nacos 控制台中 服务管理 | 服务列表 中查看启动的服务是否已经在列表中了,在说明服务已经注册成功了,可以在控制台中管理该服务了

七、Gateway

Spring Cloud Gateway,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接,如 WebSockets。
Spring Cloud Gateway 使用非阻塞 API,支持 WebSockets,支持限流等新特性。

1.网关的路由配置

spring:
  application:
    name: gateway-client
  cloud:
    gateway:
      routes:
      - id: route_server_appsdk
        # uri以lb://开头(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称
        uri: lb://server_appsdk
        predicates:
        - Path=/api/user/**

id:我们自定义的路由 ID,保持唯一
uri:目标服务地址
predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)
- Path 通过请求路径匹配
filters:过滤规则

2.过滤器 filter

过滤器可以在路由请求之前对请求进行处理,也可以在请求响应之后对响应进行处理
在请求路由之前可以做比如参数校验,鉴权,日志记录,协议转换,请求参数修改,路径修改等
在请求响应之后可以做比如记录响应消息,修改响应,修改响应头等
过滤器最常见的功能就是鉴权,日志记录,限流和权重路由
Filter分为Gateway Filter和Global Filter

Global Filter是全局的过滤器,配置之后对所有路由都有效

过滤器 说明
LoadBalancerClientFilter 负载均衡过滤器
Netty Routing Filter 默认使用netty的底层
RouteToRequestUrlFilter 新的请求路由
Websocket Routing Filter websocket路由
Gateway Metrics Filter 路由监控,配合spring-boot-starter-actuator

自定义全局过滤器

@Slf4j
@Component
public class MyFilter implements GlobalFilter, Ordered {
    @Override
    public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("this is a pre filter");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("this is a post filter");
        }));
    }

    @Override
    public int getOrder() {
        return -1;
    }
}

注意:

1.这里的order不能使用@order 的注解

2.在fitler里面的是pre类型,会在路由前执行,在then里面的是post类型会在路由后执行

3.order 的数值越小pre越先执行,post越后执行

你可能感兴趣的:(一个精简的微服务项目总结)