一个提供SDK接口的精简微服务项目的总结,使用的是SSM(springboot + springMVC + Mybatis)框架,配合Nacos进行服务发现和注册以及服务配置,项目主要包括两个服务:一个网关服务(server_gateway)和一个接口服务(server_appsdk)。
一、IDEA环境相关
Maven Helper 插件安装
操作maven 命令,如使用maven 打包等Lombok 插件安装
lombok 能够减少大量的模板代码,使用
@Data 注解时可以不用书写
getter,setter方法,
toString方法
hashCode方法
equals方法等
@NoArgsConstructor, @AllArgsConstructor 可以不用写构造函数
需要添加依赖
org.projectlombok
lombok
true
MyBatisCodeHelperPro 插件安装
设计完表后可以使用该插件自动生成dao层的表对应实体domain、mapper、以及mapper对应sql的xml文件,
也可以生成service这个看个人需要,前面生成的dao层非常好用如果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.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越后执行