1. Dew-Framework
对Spring Cloud/Boot的封装扩展、整合公司现有能力、提供最佳实践,做为基础服务框架,支撑公司新项目地研发。
Dew [du:] 意为`露水`,希望此框架可以像晨间的露水一样透明、静谧、丰盈。让使用者尽量不要感知框架的存在,专注业务实现。
2. 使用手册
使用申明
|
Dew 框架是对 Spring Boot/Cloud 的扩展,使用之前务必了解相关框架的基础知识。
|
本手册只介绍Dew 扩展的功能!
|
2.1. 结构说明
此章节关联示例:bone-example
Dew
所有模块均为Maven结构,使用如下:
<!--引入Dew父依赖,也可以使用import方式-->
<parent>
<groupId>com.tairanchina.csp.dew</groupId>
<artifactId>parent-starter</artifactId>
<!--生产环境请选择合适的版本!!!!!!!!-->
<version>${dew.version}</version>
</parent>
...
<dependencies>
<!--引入需要的模块-->
<dependency>
<groupId>com.tairanchina.csp.dew</groupId>
<artifactId><模块名></artifactId>
</dependency>
</dependencies>
...
<!--开发者-->
<developers>
<developer>
<name></name>
<email></email>
</developer>
</developers>
<!--SCM信息-->
<scm>
<connection></connection>
<developerConnection></developerConnection>
<url></url>
</scm>
parent-starter 中已包含各模块的版本,引用模块依赖时可省略版本号。
|
Dew
推荐的代码结构为:
|- common-service | |- src | | |- main | | | |- java | | | | |- src | | | | | |- x.y.z | | | | | | |- XApplication (1) | | | | | | |- XConfig (2) | | | | | | |- XInitiator (3)
1 | XApplication : 服务启动类 |
2 | XConfig:当前服务的配置文件(Spring Cloud格式) |
3 | XInitiator:当前服务启动初始化器,原则上所有初始化操作都应该从此类发起,如初始化脚本、定时任务等,以方便排错 |
2.2. 功能模块
模块名 | 核心功能 |
---|---|
|
Dew的工程父依赖 |
|
基于Spring Boot的扩展,Dew的核心模块 |
|
基于Spring Cloud的扩展 |
|
幂性处理模块 |
|
集群能力接口 |
|
集群能力-Redis实现 |
|
集群能力-Hazelcast实现 |
|
集群能力-Rabbit实现 |
|
集群能力-Eureka实现 |
|
Dew的单元测试封装 |
|
统一配置中心组件 |
|
服务注册中心组件 |
|
服务网关组件 |
|
服务监控组件 |
|
多租户的权限组件(Scala语言编写) |
|
各类示例 |
2.3. 核心功能
2.3.1. 常用工具集
Dew
框架的常用工具由 Dew-Common
包提供( https://github.com/gudaoxuri/dew-common ),功能如下:
-
Json与Java对象互转,支持泛型
-
Java Bean操作,Bean复制、反射获取/设置注解、字段、方法等
-
Java Class扫描操作,根据注解或名称过滤
-
Shell脚本操作,Shell内容获取、成功捕获及进度报告等
-
加解密操作,Base64、MD5/BCrypt/SHA等对称算法和RSA等非对称算法
-
Http操作,包含Get/Post/Put/Delete/Head/Options操作
-
金额操作,金额转大写操作
-
通用拦截器栈,前/后置、错误处理等
-
定时器操作,定时和周期性任务
-
常用文件操作,根据不同情况获取文件内容
-
常用字段操作,各类字段验证、身份证提取、UUID创建等
-
常用时间处理,常规时间格式化模板
-
主流文件MIME整理,MIME分类
-
响应处理及分页模型
Dew Common 的使用
|
2.3.2. 集群功能
此章节关联示例:cluster-example
Dew
的集群支持 分布式缓存
分布式Map
分布式锁
MQ
领导者选举
,
并且做了接口抽象以适配不同的实现,目前支持 Redis
Hazelcast
Rabbit
Eureka
。
各实现对应的支持如下:
功能 | Redis | Hazelcast | Rabbit | Eureka |
---|---|---|---|---|
分布式缓存 |
* |
/ |
/ |
/ |
分布式Map |
* |
* |
/ |
/ |
分布式锁 |
* |
* |
/ |
/ |
MQ |
* |
* |
* |
/ |
领导者选举 |
* |
/ |
/ |
* |
各实现的差异
|
使用
<dependency>
<groupId>com.tairanchina.csp.dew</groupId>
<artifactId>boot-starter</artifactId>
</dependency>
<!--引入集群依赖,可选redis/hazelcast/rabbit/eureka-->
<dependency>
<groupId>com.tairanchina.csp.dew</groupId>
<artifactId>cluster-spi-redis</artifactId>
</dependency>
<dependency>
<groupId>com.tairanchina.csp.dew</groupId>
<artifactId>cluster-spi-hazelcast</artifactId>
</dependency>
<dependency>
<groupId>com.tairanchina.csp.dew</groupId>
<artifactId>cluster-spi-rabbit</artifactId>
</dependency>
<!--此实现需要引用 cloud-starter -->
<dependency>
<groupId>com.tairanchina.csp.dew</groupId>
<artifactId>cluster-spi-eureka</artifactId>
</dependency>
dew:
cluster: # 集群功能
cache: # 分布式缓存实现,默认为 redis
map: # 分布式Map实现,默认为 redis
lock: # 分布式锁实现,默认为 redis
mq: # MQ实现,默认为 redis
election: # 领导者选举实现,默认为 redis
spring:
redis:
host: # redis主机
port: # redis端口
database: # redis数据库
password: # redis密码
pool: # 连接池配置
rabbitmq:
host: # rabbit主机
port: # rabbit端口
username: # rabbit用户名
password: # rabbit密码
virtual-host: # rabbit VH
hazelcast:
addresses: [] # hazelcast地址,端口可选
eureka 实现了领导者选择,必须为 Spring Cloud 工程。
|
集群服务的使用入口统一为: Dew.cluster.XX
分布式缓存
接口见:com.tairanchina.csp.dew.core.cluster.ClusterCache |
Dew.cluster.cache.flushdb();
Dew.cluster.cache.del("n_test");
assert !Dew.cluster.cache.exists("n_test");
Dew.cluster.cache.set("n_test", "{\"name\":\"jzy\"}", 1);
assert Dew.cluster.cache.exists("n_test");
assert "jzy".equals($.json.toJson(Dew.cluster.cache.get("n_test")).get("name").asText());
Thread.sleep(1000);
assert !Dew.cluster.cache.exists("n_test");
assert null == Dew.cluster.cache.get("n_test");
Dew的缓存默认只实现了String、List、Set、Hash等结构常用的,时间复杂度低的操作,
如需要的操作Dew没有提供可使用Spring Boot Data Redis原生的RedisTemplate<String,String>
|
分布式Map
接口见:com.tairanchina.csp.dew.core.cluster.ClusterMap |
ClusterMap<TestMapObj> mapObj = Dew.cluster.map.instance("test_obj_map", TestMapObj.class);
mapObj.clear();
TestMapObj obj = new TestMapObj();
obj.a = "测试";
mapObj.put("test", obj);
assert "测试".equals(mapObj.get("test").a);
分布式锁
接口见:com.tairanchina.csp.dew.core.cluster.ClusterLock |
// dist lock
ClusterLock lock = Dew.cluster.lock.instance("test_lock");
// tryLock 示例,等待0ms,忘了手工unLock或出异常时1s后自动解锁
if (lock.tryLock(0, 1000)) {
try {
// 已加锁,执行业务方法
} finally {
// 必须手工解锁
lock.unLock();
}
}
// tryLockWithFun 示例
lock.tryLockWithFun(0, 1000, () -> {
// 已加锁,执行业务方法,tryLockWithFun会将业务方法包裹在try-cache中,无需手工解锁
});
MQ
接口见:com.tairanchina.csp.dew.core.cluster.ClusterMQ |
// pub-sub
Dew.cluster.mq.subscribe("test_pub_sub", message ->
logger.info("pub_sub>>" + message));
Thread.sleep(1000);
Dew.cluster.mq.publish("test_pub_sub", "msgA");
Dew.cluster.mq.publish("test_pub_sub", "msgB");
// req-resp
Dew.cluster.mq.response("test_rep_resp", message ->
logger.info("req_resp>>" + message));
Dew.cluster.mq.request("test_rep_resp", "msg1");
Dew.cluster.mq.request("test_rep_resp", "msg2");
// rabbit confirm
if (Dew.cluster.mq instanceof RabbitClusterMQ) {
boolean success = ((RabbitClusterMQ) Dew.cluster.mq).publish("test_pub_sub", "confirm message", true);
success = ((RabbitClusterMQ) Dew.cluster.mq).request("test_rep_resp", "confirm message", true);
}
发布订阅模式时,发布前 topic 必须已经存在,可先使用 subscribe 订阅,此操作会自动创建 topic 。
|
Rabbit 实现支持单条 confirm 模式。
|
MQ的HA(高可用)支持,默认MQ启用HA(可通过dew.cluster.config.ha-enabled=false
关闭)。
可实现。
Dew的MQ仅在数据处理完成后才做commit,这限制了对同一个队列只能串行处理, MQ的HA开启后,您可以以多线程的方式消费消息,处理过程中如发生服务宕机重启后仍可从未处理完成的消息开始消费。
2.3.3. 幂等处理
此章节关联示例:idempotent-example
支持HTTP和非HTTP幂等操作,对于HTTP操作,要求请求方在请求头或URL参数中加上操作ID标识,非HTTP操作由可自由指定操作类型和操作ID标识的来源。
<!--引入幂等支持-->
<dependency>
<groupId>com.tairanchina.csp.dew</groupId>
<artifactId>idempotent-starter</artifactId>
</dependency>
dew:
cluster:
cache: redis # 启用Redis支持
idempotent:
default-expire-ms: 3600000 # 设置默认过期时间,1小时
default-strategy: item # 设置默认策略,支持 bloom(Bloom Filter)和item(逐条记录),目前只支持item
default-opt-id-flag: __IDEMPOTENT_OPT_ID__ # 指定幂等操作ID标识,可以位于HTTP Header或请求参数中
@GetMapping(xxx)
// 启用幂等支持
// 请求头部或参数加上__IDEMPOTENT_OPT_ID__ = xx
@Idempotent
public void test(xxx) {
// 业务操作
// ...
// 业务失败,在保证业务操作的原子性的情况下,在catch中取消幂等,并抛出异常
DewIdempotent.cancel();
// 手工确认
DewIdempotent.confirm();
}
Idempotent
注解说明:
-
optIdFlag:指定幂等操作ID标识,可以位于HTTP Header或请求参数中
-
expireMs:设置过期时间,单位毫秒
-
strategy:设置默认策略
-
needConfirm:设置是否需要显式确认,true时,需要进行显式确认操作:
DewIdempotent.confirm() 或 DewIdempotent.confirm(String optType, String optId)
前者要求与请求入口在同一线程中
// 初始化类型为transfer_a的幂等操作,需要手工确认,过期时间为1秒
DewIdempotent.initOptTypeInfo("transfer_a", true, 1000, StrategyEnum.ITEM);
// 第一次请求transfer_a类型下的xxxxxxx这个ID,返回不存在,表示可以下一步操作
Assert.assertEquals(StatusEnum.NOT_EXIST, DewIdempotent.process("transfer_a", "xxxxxxx"));
// 第二次请求transfer_a类型下的xxxxxxx这个ID,返回未确认,表示上次操作还在进行中
Assert.assertEquals(StatusEnum.UN_CONFIRM, DewIdempotent.process("transfer_a", "xxxxxxx"));
// 确认操作完成
DewIdempotent.confirm("transfer_a", "xxxxxxx");
// 第三次请求transfer_a类型下的xxxxxxx这个ID,返回已确认,但未过期,仍不能操作
Assert.assertEquals(StatusEnum.CONFIRMED, DewIdempotent.process("transfer_a", "xxxxxxx"));
// 延时1秒
Thread.sleep(1000);
// 再次请求transfer_a类型下的xxxxxxx这个ID,返回不存在(上次请求已过期),表示可以下一步操作
Assert.assertEquals(StatusEnum.NOT_EXIST, DewIdempotent.process("transfer_a", "xxxxxxx"));
2.3.4. 统一响应
Dew
推荐使用 协议无关的响应格式
,此格式在 方法间调用
非HTTP协议RPC
MQ
等数据交互场景做到真正的 统一响应格式
。
要求返回的格式为Resp
对象,格式为:
{ code: "", // 响应编码,与http状态码类似,200表示成功 message:"", // 响应附加消息,多有于错误描述 body: // 响应正文 }
public Resp<String> test(){
return Resp.success("enjoy!");
// or return Resp.notFound("…")/conflict("…")/badRequest("…")/…
}
Resp
类提供了常用操作:详见 https://gudaoxuri.github.io/dew-common/#true-resp
Dew
使用返回格式中的code表示操作状态码,此状态码与HTTP状态码无关,一般情况下HTTP状态码均为200,如需要降级处理时返回500。
500 Http状态码说明
|
2.3.5. 消息通知
Dew
支持发送消息到钉钉或邮件,默认支持对未捕获异常及Hystrix错误的通知。
# 格式
dew:
notifies:
"": # 通知的标识
type: DD # 通知的类型,DD=钉钉 MAIL=邮件,邮件方式需要有配置spring.mail下相关的smtp信息 HTTP=自定义HTTP Hook
defaultReceivers: # 默认接收人列表,钉钉为手机号,邮件为邮箱
dndTimeReceivers: # 免扰时间内的接收人列表,只有该列表中的接收人才能在免扰时间内接收通知
args: # 不同类型的参数,邮件不需要设置
url: # 钉钉及自定义HTTP的推送地址,钉钉URL说明详见:https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.karFPe&treeId=257&articleId=105735&docType=1
strategy: # 通知策略
minIntervalSec: 0 # 最小间隔的通知时间,0表示不设置,如为10则表示10s内只会发送一次
dndTime: # 开启免扰时间,HH:mm-HH:mm 如,18:00-06:00
forceSendTimes: 3 # 同一免扰周期间通知调用达到几次后强制发送
# 示例
dew:
notifies:
__DEW_ERROR__:
type: DD
defaultReceivers: xxxx
args:
url: https://oapi.dingtalk.com/robot/send?access_token=8ff65c48001c1981df7d3269
strategy:
minIntervalSec: 5
sendMail:
type: MAIL
defaultReceivers: x@y.z
custom:
type: HTTP
defaultReceivers: x@y.z
args:
url: https://...
# 最简单的调用
Resp<Void> result = Dew.notify.send("<通知的标识>", "<通知的内容或Throwable>");
# 带通知标题,标题会加上``Dew.Info.instance``
Resp<Void> result = Dew.notify.send("<通知的标识>", "<通知的内容或Throwable>", "<通知标题>");
# 加上特殊接收人列表,非免扰时间内的接收人=配置默认接收人列表+特殊接收人列表,免扰时间内的接收人=配置的免扰时间内的接收人列表
Resp<Void> result = Dew.notify.send("<通知的标识>", "<通知的内容或Throwable>", "<通知标题>", "<特殊接收人列表>");
# 上述三个方法都有异步的重载方法,如
Dew.notify.sendAsync("<通知的标识>", "<通知的内容或Throwable>");
默认通知标识
要启用以上两个通知请确保 |
HTTP自定义通知格式
POST请求,Body格式为: { "title": "", // 标题 "content": "", // 内容 "receivers": [] // 接收人列表 } 调用正常需要返回200状态码 |
2.3.6. 异常处理
Dew
会把程序没有捕获的异常统一上抛,同时框架提供了常用的异常检查:
Dew.E.check(VoidPredicate notExpected, E ex)
Dew.E.check(boolean notExpected, E ex)
Dew.E.checkNotEmpty(Map<?, ?> objects, E ex)
Dew.E.checkNotEmpty(Iterable<?> objects, E ex)
Dew.E.checkNotNull(Object obj, E ex)
dew:
basic:
error-mapping:
"[<异常类名>]":
http-code: # http状态码,不存在时使用实例级http状态码
business-code: # 业务编码,不存在时使用实例级业务编码
message: # 错误描述,不存在时使用实例级错误描述
2.3.7. 数据验证
Dew
集成了Spring validate
机制,支持针对 URL
及 Bean
的验证。
-
在 java bean 中添加各项validation,支持标准`javax.validation.constraints`包下的诸如:
NotNull
,同时框架扩展了几个检查,如: IdNumber、Phone -
在Controller中添加
@Validated
注解 ( Spring还支持@Vaild,但这一注解不支持分组 ) -
支持Spring原生分组校验
-
URL
类型的验证必须在类头添加@Validated
注解 -
Dew
框架内置了CreateGroup
UpdateGroup
两个验证组,验证组仅是一个标识,可为任何java对象
2.3.8. CORS支持
dew:
security:
cors:
allow-origin: # 允许来源,默认 *
allow-methods: # 允许方法,默认 POST,GET,OPTIONS,PUT,DELETE,HEAD
allow-headers: # 允许头信息 x-requested-with,content-type
2.3.9. 权限认证
此章节关联示例:auth-example
支持`认证缓存`,即支持将鉴权系统生成的登录信息缓存到业务系统中方便即时调用,并提供三方适配。
dew:
security:
token-flag: # token key的名称
token-in-header: # token key是否在http header中,为false是会从url query中获取
token-hash: # token 值是否做hash(MD5)处理
认证缓存需要 集群缓存 服务支持,请引入相关的依赖并配置对应的连接信息等。
|
// 添加登录信息,optInfo封装自鉴权系统过来的登录信息
// 一般在登录认证后操作
Dew.auth.setOptInfo(OptInfo optInfo);
// 获取登录信息,要求在http请求加上token信息
Dew.context().optInfo();
// 删除登录信息
// 一般在注销登录后操作
Dew.auth.removeOptInfo();
// 登录信息
public class OptInfo {
// Token
String token;
// 账号编码
String accountCode;
}
OptInfo 为认证缓存信息的基类,使用时可以继承并扩展自己的属性。
|
使用 OptInfo 扩展类型时需要在工程启动时指定扩展类: DewContext.setOptInfoClazz(<扩展类型>) 。
|
/**
* 模拟用户注册
*/
@PostMapping(value = "user/register")
public Resp<Void> register(@RequestBody User user) {
// 实际注册处理
user.setId($.field.createUUID());
MOCK_USER_CONTAINER.put(user.getId(), user);
return Resp.success(null);
}
/**
* 模拟用户登录
*/
@PostMapping(value = "auth/login")
public Resp<String> login(@RequestBody LoginDTO loginDTO) {
// 实际登录处理
User user = MOCK_USER_CONTAINER.values().stream().filter(u -> u.getIdCard().equals(loginDTO.getIdCard())).findFirst().get();
String token = $.field.createUUID();
Dew.auth.setOptInfo(new OptInfoExt()
.setIdCard(user.getIdCard())
.setAccountCode($.field.createShortUUID())
.setToken(token)
.setName(user.getName())
.setMobile(user.getPhone()));
return Resp.success(token);
}
/**
* 模拟业务操作
*/
@GetMapping(value = "business/someopt")
public Resp<Void> someOpt() {
// 获取登录用户信息
Optional<OptInfoExt> optInfoExtOpt = Dew.auth.getOptInfo();
if (!optInfoExtOpt.isPresent()) {
return Resp.unAuthorized("用户认证错误");
}
// 登录用户的信息
optInfoExtOpt.get();
return Resp.success(null);
}
/**
* 模拟用户注销
*/
@DeleteMapping(value = "auth/logout")
public Resp<Void> logout() {
// 实际注册处理
Dew.auth.removeOptInfo();
return Resp.success(null);
}
2.3.10. 测试支持
良好的单元测试可以保证代码的高质量,单测的重要原则是内聚、无依赖,好的单测应该是"函数化"的——结果的变化只与传入参数有关。
但实际上我们会的代码往往会与数据库、缓存、MQ等外部工具交互,这会使单测的结果不可控,通常的解决方案是使用Mock,但这无行中引入了单测撰写的成本,
Dew
使用"内嵌式"工具解决,数据库使用 H2
,Redis使用 embedded redis
,由于 Dew
集群的 Cache
Map
Lock
MQ
都支持 Redis
实现,所以可以做到对主流操作的全覆盖。
<dependency>
<groupId>com.tairanchina.csp.dew</groupId>
<artifactId>test-starter</artifactId>
</dependency>
dew:
cluster: #所有集群操作都使用reids模拟
cache: redis
lock: redis
map: redis
mq: redis
spring:
redis:
host: 127.0.0.1
port: 6379
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:test
2.4. 工程化
2.4.1. API文档
用于生成Asciidoc格式的离线API文档,可通过软件转换成HTML及PDF版本。
dew:
basic:
name: # 文档名称
version: 1.0 # 文档版本
desc: # 文档说明
web-site: # 文档站点
doc:
base-package: # API文档要扫描的根包,多指定到 Controller 包中
contact: # 联系人信息,可为空
name: # 联系人姓名
url: # 联系人URL
email: # 联系人邮箱
management: # 文档生成地址前缀为 /management.context-path
security:
enabled: false # 建议关闭管理功能的安全认证
context-path: /management-admin # 建议修改此前缀以便在网关上统一过滤管理请求
访问任意服务实例:
PUT /{management.context-path}/doc/offline
{
"docName":"", # 文档名称
"docDesc":"", # 文档说明
"visitUrls":{ # 不同环境的访问地址
"":""
},
"swaggerJsonUrls":[] # swagger.json的URL列表(http://.../v2/api-docs),可选
}
swaggerJsonUrls 不为空时根据传入的swaggerJsonUrls合成接口文档,为空时分两种情况:
1. 如果是非集群模式(没有注册中心)则只生成本服务的接口文档
2. 如果是集群模式(注册到注册中心)则会合成此集群下所有的接口文档
示例:
{
"docName":"Dew API 文档",
"docDesc":"此为Dew的接口文档\n描述可以换行",
"visitUrls":{
"开发环境":"http://dev.dew.ecfront.com",
"测试环境":"http://test.dew.ecfront.com"
}
}
调用此接口后返回Asciidoc格式的文档内容,复制此内容到相应的转换工具中即可生成对应的HTTP或PDF文档。
2.4.2. 代码质量检查
Dew
已集成 Sonar
插件,只需要在maven中配置 sonar.host.url
为目标地址,
然后执行 mvn clean verify sonar:sonar -P qa -Dsonar.login=<用户名> -Dsonar.password=<密码>
即可。
也可以设置 sonar.forceAuthentication=false ,但要注意安全管控。
|
使用 <maven.test.skip>true</maven.test.skip> 可跳过特定模块的测试,<sonar.skip>true</sonar.skip> 可跳过特定模块的Sonar检查。
|
2.4.3. 降级通知
此章节关联 `hystrix-feign-example` 示例
为能更及时的对服务异常做出处理, dew 增加邮件通知功能。
|
# 通知条件配置示例
dew:
cloud:
error:
enabled: true
notify-event-types: FAILURE,SHORT_CIRCUITED,TIMEOUT,THREAD_POOL_REJECTED,SEMAPHORE_REJECTED
notify-include-keys: ["ExampleClient#deleteExe(int,String)","ExampleClient#postExe(int,String)"]
# 邮箱配置示例
spring:
mail:
host: smtp.163.com
username: <邮件地址>
password: <password为smtp授权码,非邮箱密码>
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
2.4.4. 跟踪日志
此章节关联 `sleuth-invokeX-example` 示例
用于记录 服务API调用
(追踪)日志到 Slf4j
。
dew:
cloud:
trace-log:
enabled: true # 默认为true
一次调用日志的查看,以 ES
为例,过滤条件是: logger:com.tairanchina.csp.dew.core.logger.TraceLogWrap & trace:<对应的traceID>
2.4.5. Spring Admin
集成
此章节关联示例:monitor-example
Dew
集成了 Spring Admin
,封装成 monitor
组件, 示例 monitor-example
演示了如何与 monitor
交互。
monitor
关键配置spring:
application:
name: monitor # 监控服务名称
boot:
admin:
routes:
endpoints: env,metrics,dump,jolokia,info,configprops,trace,logfile,refresh,flyway,liquibase,heapdump,loggers,auditevents,hystrix.stream # 要统计的内容
turbine: # turbine集成配置
clusters: default # 集群名称
location: monitor # 聚合到的服务名称,这里要与 `spring.application.name` 相同
turbine: # turbine配置
aggregator:
clusterConfig: default # 集群名称
appConfig: monitor-example # 要聚合的服务名称,需要把各个服务添加上去
clusterNameExpression: metadata['cluster']
server:
port: # 端口号
eureka:
client:
serviceUrl:
defaultZone: # eureka 服务地址
spring:
application:
name: monitor-example # 服务名称,必须在上文 `turbine.appConfig` 添加上去
eureka:
client:
serviceUrl: # eureka 服务地址,必须和监控服务在同一集群中
instance:
metadata-map:
cluster: default # 集群名称
management.security.enabled: false # 需要关闭安全管理,可通过IP来限制
3. 最佳实践
3.1. JDBC框架的选择
主流JDBC框架有Hibernate、MyBatis、Spring JDBC Template、Ebean、DBUtils等,Dew基于Spring Boot,所以对于这些框架都提供了很好的兼容。那么如何选择呢?
-
先说Hibernate,如果你的数据表结构与对象模型基本吻合,那么使用JPA会带来很大的便捷,推荐Hibernate
-
如果你的数据表结构与对象模型严重不吻合或是你希望对SQL有更多主动权(SQL优化、复杂查询等),那JPA就没什么优势了,这时:
-
如果你追求极简、不需要ORMPPING,那么DBUtils会是最佳选择
-
如果你喜欢敏捷开发,推崇充血模型,那么尝试一下Ebean吧,与Play!结合最合适不过
-
如果你既要有一定的ORMPPING能力,又希望自己写SQL,那么MyBatis会是不错的选择
-
如果你使用了Spring,希望框架简单些,可以接受自己写ORMPPING,未来无切换关系型数据库的计划,那么Spring JDBC Template将是个很好的选择
-
3.2. 服务调用开发期使用
在 Spring Cloud
体系下,服务调用需要启动 Eureka
服务(对于 Dew
中的 Registry
组件),这对开发阶段并不友好:
-
开发期间会不断启停服务,
Eureka
保护机制会影响服务注册(当然这是可以关闭的) -
多人协作时可能会出现调用到他人服务的情况(同一服务多个实例)
-
需要启动
Eureka
服务,多了一个依赖
为解决上述问题,在使用 Spring Cloud
的 RestTemplate
时,增加 Ribbon
的服务配置.
# <client>为service-id <client>.ribbon.listOfServers: <直接访问的IPs> # 如 x-service.ribbon.listOfServers: 127.0.0.1:8812
3.3. @Validated
注解
-
在Spring Controller类里,
@Validated
注解初使用会比较不易上手,在此做下总结-
对于基本数据类型和String类型,要使校验的注解生效,需在该类上方加
@Validated
注解 -
对于抽象数据类型,需在形式参数前加
@Validated
注解
-
Spring对抽象数据类型校验抛出异常为MethodArgumentNotValidException ,http状态码为400,对基本数据类型校验抛出异常为ConstraintViolationException ,http状态码为500,dew对这两种异常做了统一处理,http状态码均返回200,code为400
|
3.4. jackson
对于 Java8
时间转换( SpringMVC
以 jackson
接收 json
数据)
-
对于
LocalDateTime
类型,需在参数上加@JsonFormat
注解,如下:@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
-
LocalDate,LocalTime,Instant
等,无需配置可自行转换
jackson 对于 LocalDateTime 类型的支持与其他三种类型不具有一致性,这是 jackson 需要优化的一个点
|
3.5. Ribbon
负载均衡
example: service-dew.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
service-dew 为服务名,配置时自行选取规则,类均在com.netflix.loadbalancer 包下
|
#指定属于哪个zone
eureka:
instance:
metadata-map:
zone: #zone 名称
#指定region(此处region为项目在不同区域的部署,为项目规范,不同region间能互相调用)
eureka:
client:
region: #region名称
3.6. Feign
配置特定方法超时时间
Hystrix
超时时间配置
# 配置默认的hystrix超时时间 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000 # 配置特定方法的超时时间,优于默认配置 hystrix.command.<hystrixcommandkey>.execution.isolation.thread.timeoutInMilliseconds=10000 # <hystrixcommandkey>的format为FeignClassName#methodSignature,下面是示例配置 hystrix.command.PressureService#getBalance(int).execution.isolation.thread.timeoutInMilliseconds=10000
Ribbon
超时时间配置
# 配置默认ribbon超时时间 ribbon.ReadTimeout=60000 # 配置特定服务超时时间,优于默认配置 <client>.ribbon.ReadTimeout=6000 # <client>为实际服务名,下面是示例配置 x-service.ribbon.ReadTimeout=5000
Hystrix
和 Ribbon
的超时时间配置相互独立,以低为准,使用时请根据实际情况进行配置
如果要针对某个服务做超时设置,建议使用 Ribbon 的配置;在同时使用 Ribbon 和 Hystrix 时,请特别注意超时时间的配置。
|
3.7. Feign
接口添加 Http
请求头信息
在 @FeignClient 修饰类中的接口方法里添加新的形参,并加上 @RequestHeader 注解指定key值
|
@PostMapping(value = "ca/all", consumes = MediaType.APPLICATION_JSON_VALUE)
Resp<CustomerInfoVO> applyCA(@RequestBody CAIdentificationDTO params,
@RequestHeader Map<String, Object> headers);
3.8. Feign
文件上传实践
-
在
SDK
工程处,添加包依赖
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>3.0.1</version>
</dependency>
-
在
SDK
工程处,创建一个Configuration
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.cloud.netflix.feign.support.SpringEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MultipartSupportConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
-
修改接口
@FeignClient(name = "demo")
public interface FeginExample {
@PostMapping(value = "images", consumes = MULTIPART_FORM_DATA_VALUE)
Resp<String> uploadImage(
@RequestParam MultipartFile image,
@RequestParam("id") String id);
}
@RequestPart
与 @RequestParam
效果是一样的,大家就不用花时间在这上面了。
-
修改服务器接口
@RestController
public class FeginServiceExample {
@PostMapping(value = "images", consumes = MULTIPART_FORM_DATA_VALUE)
public Resp<String> uploadImage(
@RequestParam("image") MultipartFile image,
@RequestParam("id") String id,
HttpServletRequest request) {
return Resp.success(null);
}
}
常见问题:
-
HTTP Status 400 - Required request part 'file' is not present
请求文件参数的名称与实际接口接受名称不一致
-
feign.codec.EncodeException: Could not write request: no suitable HttpMessageConverter found for request type [org.springframework.mock.web.MockMultipartFile] and content type [multipart/form-data]
转换器没有生效,检查一下MultipartSupportConfig
3.9. 自定义降级方法
构建类继承HystrixCommand抽象类,重写run方法,getFallback方法,getFallback为run的降级,再执行excute方法即可 |
每个HystrixCommand的子类的实例只能execute一次。 |
public class HelloHystrixCommand extends HystrixCommand<HelloHystrixCommand.Model> {
public static final Logger logger = LoggerFactory.getLogger(HelloHystrixCommand.class);
private Model model;
protected HelloHystrixCommand(HystrixCommandGroupKey group) {
super(group);
}
protected HelloHystrixCommand(HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool) {
super(group, threadPool);
}
protected HelloHystrixCommand(HystrixCommandGroupKey group, int executionIsolationThreadTimeoutInMilliseconds) {
super(group, executionIsolationThreadTimeoutInMilliseconds);
}
protected HelloHystrixCommand(HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool, int executionIsolationThreadTimeoutInMilliseconds) {
super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
}
protected HelloHystrixCommand(Setter setter) {
super(setter);
}
public static HelloHystrixCommand getInstance(String key){
return new HelloHystrixCommand(HystrixCommandGroupKey.Factory.asKey(key));
}
@Override
protected Model run() throws Exception {
int i = 1 / 0;
logger.info("run: thread id: " + Thread.currentThread().getId());
return model;
}
@Override
protected Model getFallback() {
return new Model("fallback");
}
public static void main(String[] args) throws Exception {
HelloHystrixCommand helloHystrixCommand = HelloHystrixCommand.getInstance("dew");
helloHystrixCommand.model = helloHystrixCommand.new Model("run");
logger.info("main: " + helloHystrixCommand.model + "thread id: " + Thread.currentThread().getId());
System.out.println(helloHystrixCommand.execute());
}
class Model {
public Model(String name) {
this.name = name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Model{" +
"name='" + name + '\'' +
'}';
}
}
}
3.10. 断路保护
# 执行的隔离策略 THREAD, SEMAPHORE 默认THREAD
hystrix.command.default.execution.isolation.strategy=THREAD
# 执行hystrix command的超时时间,超时后会进入fallback方法 默认1000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
# 执行hystrix command是否限制超时,默认是true
hystrix.command.default.execution.timeout.enabled=true
# hystrix command 执行超时后是否中断 默认true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout=true
# 使用信号量隔离时,信号量大小,默认10
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10
# fallback方法最大并发请求数 默认是10
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=10
# 服务降级是否开启,默认为true
hystrix.command.default.fallback.enabled=true
# 是否使用断路器来跟踪健康指标和熔断请求
hystrix.command.default.circuitBreaker.enabled=true
# 熔断器的最小请求数,默认20. (这个不是很理解,欢迎补充)
hystrix.command.default.circuitBreaker.requestVolumeThreshold=20
# 断路器打开后的休眠时间,默认5000
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
# 断路器打开的容错比,默认50
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
# 强制打开断路器,拒绝所有请求. 默认false, 优先级高于forceClosed
hystrix.command.default.circuitBreaker.forceOpen=false
# 强制关闭断路器,接收所有请求,默认false,优先级低于forceOpen
hystrix.command.default.circuitBreaker.forceClosed=false
# hystrix command 命令执行核心线程数,最大并发 默认10
hystrix.threadpool.default.coreSize=10
-
信息参见:
使用断路保护可有效果的防止系统雪崩,Spring Cloud
对 Hystrix
做了封装,详见: http://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients
需要说明的是 Hystrix
使用新线程执行代码,导致 Threadlocal
数据不能同步,使用时需要将用到的数据做为参数传入,如果需要使用 Dew
框架的上下文(请求链路/用户等获取)需要先传入再设值,e.g.
public class HystrixExampleService {
@HystrixCommand(fallbackMethod = "defaultFallback", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")
})
public String someMethod(Map<String, Object> parameters, DewContext context) {
// !!! Hystrix使用新线程执行代码,导致Threadlocal数据不能同步,
// 使用时需要将用到的数据做为参数传入,如果需要使用Dew框架的上下文需要先传入再设值
DewContext.setContext(context);
try {
Thread.sleep(new Random().nextInt(3000));
logger.info("Normal Service Token:" + Dew.context().getToken());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "ok";
}
// 降级处理方法定义
public String defaultFallback(Map<String, Object> parameters, DewContext context, Throwable e) {
DewContext.setContext(context);
logger.info("Error Service Token:" + Dew.context().getToken());
return "fail";
}
}
3.11. 定时任务
使用 Spring Config
配置中心 refresh
时,在 @RefreshScope
注解的类中, @Scheduled
注解的自动任务会失效。
建议使用实现 SchedulingConfigurer
接口的方式添加自动任务。
@Configuration
@EnableScheduling
public class SchedulingConfiguration implements SchedulingConfigurer {
private Logger logger = LoggerFactory.getLogger(SchedulingConfiguration.class);
@Autowired
private ConfigExampleConfig config;
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(() -> logger.info("task1: " + config.getVersion()), triggerContext -> {
Instant instant = Instant.now().plus(5, SECONDS);
return Date.from(instant);
});
taskRegistrar.addTriggerTask(() -> logger.info("task2: " + config.getVersion()), new CronTrigger("1/3 * * * * ?"));
}
}
3.12. 主要性能影响参数
-
内置
Tomcat
参数调整效果并不大,如果需要调整,建议适当调大max-treads
和accept-count
# 最大等待请求数 默认100 server.tomcat.accept-count=1000 # 最大并发数 默认200 server.tomcat.max-threads=1000 # 最大连接数 默认BIO:200 NIO:10000 APR:8192 server.tomcat.max-connections=2000
-
Zuul
性能参数说明# 连接池最大连接,默认是200 zuul.host.maxTotalConnections=1000 每个route可用的最大连接数,默认值是20 zuul.host.maxPerRouteConnections=1000 Hystrix最大的并发请求 默认值是100 zuul.semaphore.maxSemaphores=1000
Zuul 的最大并发数主要调整 maxSemaphores 优先级高于 Hystrix 的最大线程数配置.
|
-
Ribbon
性能参数说明调整MaxTotalConnections
和MaxConnectionsPerHost
时建议同比调整Pool
相关的参数# ribbon 单主机最大连接数,默认50 ribbon.MaxConnectionsPerHost=500 # ribbon 总连接数,默认 200 ribbon.MaxTotalConnections=1000 # 默认200 ribbon.PoolMaxThreads=1000 # 默认1 ribbon.PoolMinThreads=500
Zuul 和其它使用 Ribbon 的服务一样,TPS主要调整 Ribbon 的 MaxConnectionsPerHost 和 MaxTotalConnections
|
-
Hystrix
性能参数说明# 并发执行的最大线程数,默认10 hystrix.threadpool.default.coreSize=100
普通 Service 使用 Hystrix 时,最大并发主要调整 hystrix.threadpool.default.coreSize
|
Hystrix 的默认超时时间为1s,在高并发下容易出现超时,建议将默认超时时间适当调长,
特殊接口需要将时间调短或更长的,使用特定配置,见上面 Feign 配置特定方法超时时间.
|
3.13. Zuul
保护(隐藏)内部服务的 Http
接口
在yml配置文件里配置(ignored-patterns
,ignored-services
)这两项中的一项即可
zuul: #配置一项即可!
ignored-patterns: /dew-example/** #排除此路径
ignored-services: dew-example #排除此服务
3.14. 缓存处理
Spring Cache
提供了很好的注解式缓存,但默认没有超时,需要根据使用的缓存容器特殊配置
@Bean
RedisCacheManager cacheManager() {
final RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
redisCacheManager.setUsePrefix(true);
redisCacheManager.setDefaultExpiration(<过期秒数>);
return redisCacheManager;
}
3.15. Spring Boot Admin 监控实践
在 Spring Boot Actuator
中提供很多像 health
、 metrics
等实时监控接口,可以方便我们随时跟踪服务的性能指标。
Spring Boot
默认是开放这些接口提供调用的,那么就问题来了,如果这些接口公开在外网中,很容易被不法分子所利用,这肯定不是我们想要的结果。
在这里我们提供一种比较好的解决方案
-
被监控的服务配置
management:
security:
enabled: false # 关闭管理认证
context-path: /management (1)
eureka:
instance:
metadata-map:
cluster: default (2)
1 | 管理前缀 |
2 | 集群名称 |
-
Zuul
网关配置
zuul:
ignoredPatterns: /*/management/** (1)
1 | 同上文 management.context-path , 这里之所以不是 /management/** ,由于网关存在项目前缀,需要往前一级,大家可以具体场景具体配置 |
-
Spring Boot Admin
配置
spring:
application:
name: monitor
boot:
admin:
discovery:
converter:
management-context-path: ${management.context-path}
routes:
endpoints: env,metrics,dump,jolokia,info,configprops,trace,logfile,refresh,flyway,liquibase,heapdump,loggers,auditevents,hystrix.stream,turbine.stream (1)
turbine:
clusters: default (2)
location: ${spring.application.name}
turbine:
instanceUrlSuffix: ${management.context-path}/hystrix.stream
aggregator:
clusterConfig: default (2)
appConfig: monitor-example,hystrix-example (3)
clusterNameExpression: metadata['cluster']
security:
basic:
enabled: false
server:
port: ...
eureka:
instance:
metadata-map:
cluster: default (2)
client:
serviceUrl:
defaultZone: ...
management:
security:
enabled: false
context-path: /management (4)
1 | 要监控的内容 |
2 | 要监控的集群名称 |
3 | 添加需要被监控的应用 Service-Id ,以逗号分隔 |
4 | 同上文 management.context-path |
3.16. jdbc 批量插入性能问题
如果不开启rewriteBatchedStatements=true,那么jdbc会把批量插入当做一行行的单条处理,也就没有达到批量插入的效果
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/dew?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
username: root
password: 123456
-
对于一张七列的表,插入1500条数据,分别对mybatis和jdbctemplate进行测试,记录三次数据如下,可以看到,该配置对于jdbctemplate影响是极大的,而对于mybatis影响却不大,后续有时间再继续深入了解
rewriteBatchedStatements | mybatis(ms) | jdbctemplate(ms) | dew(ms) |
---|---|---|---|
true |
401 |
88 |
174 |
true |
427 |
78 |
167 |
true |
422 |
75 |
176 |
false |
428 |
1967 |
2065 |
false |
410 |
2641 |
2744 |
false |
369 |
2299 |
2398 |
3.17. http请求并发数性能瓶颈
-
当策略为Thread时(默认是Thread),hystrix.threadpool.default.maximumSize为第一个性能瓶颈,默认值为10.
修改值时,需要先设置hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize为true,默认为false |
-
第二个瓶颈为springboot内置的tomcat的最大连接数,参数为server.tomcat.maxThreads,默认值为200
3.18. 日志中解析message,动态显示property
-
在启动类的main方法中注册converter,如下
PatternLayout.defaultConverterMap.put("dew", TestConverter.class.getName());
这个是解析%dew的内容 |
-
自定义Converter继承DynamicConverter<ILoggingEvent>,解析message,获取有效信息并返回解析后得到的字符串。
public class TestConverter extends DynamicConverter<ILoggingEvent> {
@Override
public String convert(ILoggingEvent event) {
// 这里未做解析,示例代码
return event.getMessage();
}
}
4. 开发指南
4.2. 环境要求
基础环境: >=java8
调试环境: 推荐使用Docker环境准备各个容器
# MySQL环境
docker run -d --name dew-mysql -p 3306:3306 -e MYSQL_DATABASE=dew -e MYSQL_ROOT_PASSWORD=123456 mysql
# Redis环境
docker run -d --name dew-redis -p 6379:6379 redis
# Rabbit环境
docker run -d --name dew-rabbit --hostname dew-rabbit -p 15671:15671 -p 15672:15672 -p 4369:4369 -p 5671:5671 -p 5672:5672 -p 25672:25672 -e RABBITMQ_DEFAULT_USER=root -e RABBITMQ_DEFAULT_PASS=123456 -e RABBITMQ_DEFAULT_VHOST=dew rabbitmq:3-management-alpine
# Hazelcast环境
docker run -d --name dew-hazelcast -p 5701:5701 hazelcast/hazelcast
# Zookeeper环境
docker run -d --name dew-zookeeper -p 2181 -t wurstmeister/zookeeper
# Kafka环境
docker run --name dew-kafka -e HOST_IP=localhost -e KAFKA_ADVERTISED_PORT=9092 -e KAFKA_BROKER_ID=1 -e ZK=zk -p 9092:9092 --link dew-zookeeper:zk -t wurstmeister/kafka
5. 编译部署
5.1. 开发期热部署
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
-
设置IDE(以Intellij IDEA为例)
-
Setting
→Build,Executiion,Deployment>Compiler
,勾选Build project automatically
-
Shift+Ctrl+Alt+/
→ 选择Registry
,出现Maintenance
窗口,勾选compiler.automake.allow.when.app.running
-
6. 配置速查
6.1. Dew
参数
dew: # Dew 参数key前缀
basic: # 基础信息
name: # 服务名称,用于API文档显示等
version: 1.0 # 服务版本号,用于API文档显示等
desc: # 服务描述,用于API文档显示等
webSite: # 官网,用于API文档显示等
doc:
enabled: true # 是否启用默认文档配置,关闭后可自定义文档管理,默认为true
base-package: # API文档要扫描的根包,多指定到 Controller 包中
contact: # 联系人信息
name: # 联系人姓名
url: # 联系人URL
email: # 联系人邮箱
format:
use-unity-error: true # 是否启用统一响应
error-flag: __DEW_ERROR__ # 默认的通知标识
error-mapping: # 自定义错误映射
"[<异常类名>]":
http-code: # http状态码,不存在时使用实例级http状态码
business-code: # 业务编码,不存在时使用实例级业务编码
message: # 错误描述,不存在时使用实例级错误描述
cluster: # 集群功能
cache: redis # 缓存实现
lock: redis # 分布式锁实现,可选 redis/hazelcast,默认redis
map: redis # 分布式Map实现,可选 redis/hazelcast,默认redis
mq: redis # MQ实现,可选 redis/hazelcast/rabbit,默认redis
election: redis # 领导者选举实现,可选 redis/eureka,默认redis
config: #集群相关配置
election-period-sec: 60 #领导者选举时间区间,默认60秒
ha-enabled: true #是否启用HA,默认为true
notifies:
"": # 通知的标识
type: DD # 通知的类型,DD=钉钉 MAIL=邮件,邮件方式需要有配置spring.mail下相关的smtp信息 HTTP=自定义HTTP Hook
defaultReceivers: # 默认接收人列表,钉钉为手机号,邮件为邮箱
dndTimeReceivers: # 免扰时间内的接收人列表,只有该列表中的接收人才能在免扰时间内接收通知
args: # 不同类型的参数,邮件不需要设置
url: # 钉钉的推送地址,说明详见:https://open-doc.dingtalk.com/docs/doc.htm?spm=a219a.7629140.0.0.karFPe&treeId=257&articleId=105735&docType=1
strategy: # 通知策略
minIntervalSec: 0 # 最小间隔的通知时间,0表示不设置,如为10则表示10s内只会发送一次
dndTime: # 免扰时间,HH:mm-HH:mm 如,18:00-06:00
forceSendTimes: 3 # 同一免扰周期间通知调用达到几次后强制发送
security: # 安全功能
cors:
allow-origin: * # 允许的来源,建议修改
allow-methods: POST,GET,OPTIONS,PUT,DELETE,HEAD # 允许的方法
allow-headers: x-requested-with,content-type # 允许的头信息
token-flag: __dew_token__ # token 标识
token-in-header: true # true:token标识在 header 中,反之在url参数中
token-hash: false # token值是否需要hash,用于解决token值有特殊字符的情况
include-services: [""] # 服务白名单,要求源服务头部带上 Request-From
exclude-services: [""] # 服务黑名单,要求源服务头部带上 Request-From
component:
auth:
token-expire-sec: 60 * 60 * 24 * 30 # token有效期,默认为30天
max-login-error-times: 3 # 最大登录错误次数,默认3次
clean-error-time-min: 10 # 登录错误限制登录时间,默认为10分钟
password-salt: # 密码加盐
email-account: # 发通知邮件的邮箱账号,比如用来发送用户注册成功邮件
sdk:
server-url: # 服务器地址
white-list: # 白名单,逗号分隔
cloud:
trace-log:
enabled: true # 是否启用服务API调用(追踪)日志,默认为true
error:
enabled: true # 启用降级邮件通知,默认为false
notify-flag: __HYSTRIX__ # 默认的通知标识
notify-event-types: FAILURE,SHORT_CIRCUITED,TIMEOUT,THREAD_POOL_REJECTED,SEMAPHORE_REJECTED # 通知的事件类型
notify-include-keys: # 需监控的方法key值,与notify-exclude-keys互斥,client类名+#+方法名,for example: ExampleClient#deleteExe(int,String)
notify-exclude-keys: # 不需要监控的方法key值,与notify-include-keys互斥,client类名+#+方法名,for example: ExampleClient#deleteExe(int,String)
idempotent:
default-expire-ms: 3600000 # 设置默认过期时间,1小时
default-strategy: item # 设置默认策略,目前支持item(逐条记录)
default-opt-id-flag: __IDEMPOTENT_OPT_ID__ # 指定幂等操作ID标识,可以位于HTTP Header或请求参数中
spring:
application:
name: # 项目名称,若使用Dew,请配置
mail: #mail配置,需要hystrix降级通知需加以下配置
host: smtp.163.com
username:
password:
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
hazelcast: # hazelcast相关配置
username:
password:
addresses: ["127.0.0.1"]
connection-timeout: #默认5000
connection-attempt-limit: #默认2
connection-attempt-period: #默认3000
7. 版本说明
7.1. 1.5.0-RC
-
使用小泰科技Fork版本做为开源版本
-
添加领导者选举的Redis实现
-
添加消息通知(钉钉或邮件)
-
添加生成系统级(多服务)统一离线文档功能
-
添加MQ消费的HA功能
-
默认使用micrometer做为指标采集工具
-
添加对Scala的支持
-
分布式锁中删除lock、lockWithFun操作
-
分布式锁由可重入改为不可重入
-
redis增加hash incr操作 和 hash decr操作
-
增加swagger-bootstrap-ui,优化swaggerUI的显示
-
spring-boot升级至1.5.13.RELEASE版本
-
spring-cloud升级到Edgware.SR4版本
-
dew-common升级到1.4.7版本
-
boot-starter默认启用HTTP服务
-
移除ShardingJDBC的内容
-
移除服务脚手架功能
-
移除mybatis-starter模块
-
暂时移除Dew JDBC模块
-
修复指标采集内存溢出问题
-
配置变更: 拆分
dew.cluster.dist
为dew.cluster.lock
和dew.cluster.map
-
配置变更:
dew.cluster.election.config.election-period-sec
todew.cluster.config.election-period-sec
-
功能变更: 领导者选举、分布式锁、分布式Map的实例化方式由
dew.cluster.election/lock/map
修改成dew.cluster.election/lock/map.instance(…)
-
功能变更: 领导者选举
isLeader
接口需要等待选举产生后再返回(之前逻辑是每次启动时会设置成false再执行选举) -
功能变更: 相同
Dew.Info.instance
的实例在选举过期周期内重启任能保持原先状态 -
功能变更: 移除服务脚手架,需要手工添加需要的接口服务
-
功能变更: 移除mybatis-starter模块,请使用mybatis官方方案
-
功能变更: swagger-ui.html 变更成 doc.html
-
功能变更:
Dew.Info.instance
由UUID
修改成服务名@Profile@IP:端口
-
功能变更: 升级后的Tomcat版本不支持Host中带有'_'这种非规范符号
7.5. 1.3.0-RC
-
#87 局部使用sharding-jdbc,mybatis实现,增加mybatis-starter模块
-
#89 支持配置提示
-
#91 Dew实例加载机制优化
-
#82 metrics指标增加线程、内存、cpu、磁盘等统计
-
#86 ErrorController增加zuul日志追踪支持
-
#92 修复logback-elasticsearch日志压力过大时导致的内存泄漏
-
修改pom.xml中dew版本号为1.3.0-RC
-
1.3.0-RC版本中已移除启动类配置,直接用
@SpringBootApplication
或@SpringCloudApplication
-
启动类需要的注解不要忘记自行添加,如
@EnableTransactionManagement
、@EnableScheduling
-
新增的mybatis-starter模块,详见使用说明
7.8. 1.2.0-RC
-
#75 添加幂等处理功能, #77 可选策略类型Bloom Filter尚在开发中
-
#72 实现针对服务整体及每个接口的TPS、最大/平均/90%响应时间Metrics统计
-
#68 支持自定义离线文档文件名
-
#70 更友好地获取本机Host
-
#76 cluster.cache 支持更多类型的操作
-
#53 统一响应——协议无关 降级由
1000
改成555
以提升兼容性 -
#79 增加是否启用默认文档配置
-
#80 增加注解启用Dew功能
-
Swagger文档去除全局token参数
-
#43 swagger2markup-maven-plugin 在使用 spring.content-path 无效
1.1.0-RC
迁移到 1.2.0-RC
-
使用
统一响应——协议无关
类型时,UI端由原来只需要获取200状态下的数据改成需要获取 200 和 555 状态下的数据,两者对UI端没有区别。( @See https://rep.360taihe.com/csp/dew-framework/issues/53 )
7.9. 1.1.0-RC
-
[功能] #45 支持服务调用(
Hystrix
)异常邮件通知 -
[功能] #51 适配新版
用户权限中心
SDK -
[功能] #59 #49 #15 统一日志规范,适配
sleuth
日志到ES
-
[优化] #53 统一响应——协议无关 类型的http返回码由统一的200改成
200
或1000
,前者表示操作成功或不需要降级的错误,后者表示需要做降级(Hystrix fallback)的错误 -
[优化] #50
Dew JDBC
更好地支持没有Entity
注解的对象 -
[优化] #52 对于java8时间,url参数转换支持String转LocalDateTime,LocalDate、LocalTime,long转LocalDateTime(但json数据不支持),long转Instant
-
[优化] #55 #58 其它一些优化
1.1.0-beta1
迁移到 1.1.0-RC
-
使用
统一响应——协议无关
类型时,UI端由原来只需要获取200状态下的数据改成需要获取 200 和 1000 状态下的数据,两者对UI端没有区别。( @See https://rep.360taihe.com/csp/dew-framework/issues/53 )
7.10. 1.1.0-beta1
-
[功能] #19 支持局部
ShardingJDBC
(由于ShardingJDBC 2.0还未RC,测试发现存在较多问题,此功能需要等待官方RC)
-
[优化] 支持Java8时间处理
-
[优化] #34 模块Spring化,
boot-core
更名为boot-starter
,cloud-core
更名为cloud-starter
-
[优化] #40
Dew JDBC
独立成jdbc-starter
, 确保核心模块boot-starter
更轻量 -
[优化]
Dew JDBC
性能优化 -
[文档] #47 添加性能调优章节
-
[修正] 统一错误拦截返回指定为
MediaType=APPLICATION_JSON_UTF8
以解决Feign
调用解码错误
1.0.0-RC/betaX
迁移到 1.1.0
1.1.0
修正了 1.0.0
版本的几个设计缺陷,需要做如下的迁移操作:
-
Maven:
Dew
框架的版本修正成1.1.0-X
,目前是1.1.0-beta1
-
Maven:
boot-core
更名为boot-starter
,cloud-core
更名为cloud-starter
-
核心代码:
com.tairanchina.csp.dew.Dew
包路径改成com.tairanchina.csp.dew.Dew
-
Dew JDBC
模块(使用MyBatis等其它持久化框架的项目可以忽略)-
SafeEntity
的创建/更新时间 由Date
换成了LocalDateTime
-
所有
entity
包 迁移到com.tairanchina.csp.dew.jdbc.entity
-
使用
JdbcTemplate
原生方法时 原来是:Dew.ds().jdbc.xx
,需要修改成((DewDS)Dew.ds).jdbc.xx
-
7.11. 1.0.0-RC
-
[功能]支持新版用户权限中心认证适配(* 新版用户权限中心Release后,此功能代码会有一定变更)
-
[功能]新增SqlBuilder用于快速构建SQL语句
-
[移除]由于 Spring Cloud Thrift RPC 测试不够充分,此版本中暂时移除
-
[功能]支持rabbit confirm(单条)模式
((RabbitClusterMQ)Dew.cluster.mq).publish(String topic, String message, boolean confirm) ((RabbitClusterMQ)Dew.cluster.mq).request(String address, String message, boolean confirm)
-
[功能]支持
EnabledColumn
结果反转,EnabledColumn用于标识是否启用状态的注解,默认是true是否用,false是禁用,但有些情况下状态字段会使用`del_flag`表示是否删除,这时需要设置结果反转 -
[功能]统一Body及Url Path/Query的异常捕获
-
[功能]
tryLock
支持重入 -
[测试]引入
embedded redis
以支持单元测试 -
[文档]添加 以宠物商店为例的
新手入门
章节 -
[修改]原
dew.dao.base-package
改成dew.jdbc.base-packages
支持多个路径
-
修正Redis锁
Unlock
处理的线程问题 -
修正jacoco单元测试覆盖率偏少的问题
7.12. 1.0.0-beta5
-
添加服务调用限制(可定义A服务不允许B服务调用,防止服务双向依赖) e.g.
dew.security.exclude-services: - serviceB - serviceC
-
添加对Thrift的支持
-
支持集群Leader Election(非严格模式)
-
整合Spring Boot Cache
-
优化CURD脚手架
-
支持UUID形式的主键
-
优化注解自定义查询(
@Select
),通过测试 -
支持自定义异常配置,见
异常处理
章节 -
添加Bean分组校验说明,见
异常处理
章节 -
添加
Sonar
代码质量检查,配置sonar.host.url
执行mvn clean verify sonar:sonar
-
【需要迁移】使用Druid数据库连接池(注意数据库连接配置变更)
-
【需要迁移】删除
DaoImpl
兼容性类 -
【需要迁移】将
Dew.e
移到Dew.E.e
,添加Dew.E.checkXX`异常检查方法,见 `异常处理
章节
-
修正事务失败,重试成功后还是被回滚的问题
7.13. 1.0.0-beta4
-
整合
Spring boot admin
与Turbine
,可直观的监控各个性能及访问指标
-
添加实验功能:使用注解自定义查询(
@Select
)
-
添加了几个自定义验证方式
-
添加性能测试报告
-
移除
DaoImpl
,改用接口DewDao
为确保兼容, DaoImpl 在这一版本中未物理移除,如有条件请迁移至 DewDao
|
7.14. 1.0.0-beta3
-
Cluster的MQ添加RabbitMQ SPI
-
支持自定义http错误码(
Dew.e(String code, E ex, StandardCode customHttpCode)
) -
对加了字段校验(@Valid)的对象,如果检验失败会返回错误详细
-
开放将ResultSet转成对象的方法(
ds.convertRsToObj(Map<String, Object> rs, Class<E> entityClazz)
)
7.15. 1.0.0-Beta2
-
支持生成Html及PDF版本的离线文档
-
添加Dubbo整合示例,提供Dubbo服务提供无法处理`声明式事务`的方案
-
完善文档并改用asciidoc格式
-
统一依赖管理
-
parent
中添加公司maven库 -
Hazelcast Client升级到3.8.2
-
Dew-Common升级到1.3.7
7.16. 1.0.0-beta1
-
多数据源支持,详见说明文档`多数据源支持`章节
原`Dew.ds.xx`接口弃用,改为`Dew.ds().xx`,如需要使用其它数据源请使用`Dew.ds(<DS Name>).xx` |
-
新增`mybatisplus-example`
-
改善`Swagger`文档支持
-
新增销毁时间支持:
boolean tryLock(long waitMillSec, long leaseMillSec)
-
锁的等待、销毁时间单位由原来的`秒`改成`毫秒`
-
修正`tryLock`锁(`Redis`实现),锁被其它线程或JVM占用时等待时间的计算错误