jmeter-接口测试
jmeter.bat 启动文件
jmeter.properties 配置文件
修改配置文件改为中文
接口测试
修改tomcat最大线程数
server:
port: 8091
tomcat:
max-threads: 10
启动测试
Sentinel服务熔断
依赖注入
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
监控平台jar下载
启动命令
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.0.jar
访问链接 http://localhost:8080/
账号密码默认是sentinel
修改配置
spring:
cloud:
sentinel:
transport:
port: 9999 #与控制台交流的端口,随意指定一个未使用的端口即可
dashboard: localhost:8080 #指定控制台服务的地址
由于sentinel控制台是软加载,需要访问接口后才会有数据展示
控制台实现接口流控
sentinel规则
流控规则
直接流控模式
- 直接流控模式是最简单的模式,当指定的接口达到限流条件时开启限流
关联流控模式
- 关联流控模式指的是,当指定接口关联的接口达到限流条件时,开启对指定接口开启限流。
- 流控模式中的关联,可以使得其它的请求服务达到阈值,本服务进行流控。
链路
第一种方式
<spring-cloud-alibaba.version>2.1.1.RELEASE</spring-cloud-alibaba.version>
@Override
//定义资源,value指定资源名称
@SentinelResource("message")
public String message() {
return "message";
}
@GetMapping("/message1")
public String message1() {
return orderService.message();
}
@GetMapping("/message2")
public String message2() {
return orderService.message();
}
修改配置 添加filter.enabled=false
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
port: 9999 #与控制台交流的端口,随意指定一个未使用的端口即可
dashboard: localhost:8080 #指定控制台服务的地址
filter:
enabled: false
添加配置文件
@Configuration
public class FilterContextConfig {
@Bean
public FilterRegistrationBean sentinelFilterRegistration(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new CommonFilter());
registrationBean.addUrlPatterns("/*");
//入口资源关闭聚合
registrationBean.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY,"false");
registrationBean.setName("sentinelFilter");
registrationBean.setOrder(1);
return registrationBean;
}
}
第二种方式
<spring-cloud-alibaba.version>2.1.3.RELEASE</spring-cloud-alibaba.version>
修改配置
server:
port: 8091
spring:
application:
name: service-order
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
port: 9999 #与控制台交流的端口,随意指定一个未使用的端口即可
dashboard: localhost:8080 #指定控制台服务的地址
web-context-unify: false
流控效果
降级规则
热点规则
热点参数流控规则是一种更细粒度的流控规则,允许将规则具体到参数上
@GetMapping("/message3")
//必须使用该注解,否则热点规则不起作用
@SentinelResource("message3")
public String message3(String name, Integer age) {
return "message3" + name + age;
}
限制第二个参数age,当age为15时提升阈值为1000
授权规则
@Component
public class RequestOriginParserDefinition implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest httpServletRequest) {
String serviceName = httpServletRequest.getParameter("serviceName");
return serviceName;
}
}
系统规则
自定义异常返回
@Component
public class ExceptionHandlerPage implements UrlBlockHandler {
@Override
public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
httpServletResponse.setContentType("application/json;charset=utf-8");
ResponseData responseData = null;
if (e instanceof FlowException) {
responseData = new ResponseData(-1, "接口被限流了");
} else if (e instanceof DegradeException) {
responseData = new ResponseData(-2, "接口被降级了");
} else if (e instanceof ParamFlowException) {
responseData = new ResponseData(-3, "接口被参数限流");
} else if (e instanceof AuthorityException) {
responseData = new ResponseData(-4, "接口授权异常");
} else if (e instanceof SystemBlockException) {
responseData = new ResponseData(-5, "系统负载异常");
} else {
responseData = new ResponseData(-6, e.toString());
}
httpServletResponse.getWriter().write(JSON.toJSONString(responseData));
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
class ResponseData {
private int code;
private String message;
}
@SentinelResource
使用方法
/**
* value 定义一个资源
* blockHandler 定义当资源内部发生了BlockException应该进入的方法
* fallback 定义当资源内部发生了Throwable应该进入的方法
*/
@SentinelResource(
value = "message2",
blockHandler = "blockHandler",
fallback = "fallback"
)
public String message2(String message) {
return message;
}
/**
* 当前方法的返回值和参数要和原方法一致
* 允许在参数列表最后加入一个BlockException参数,用来接收原方法中发生的异常
*/
public String blockHandler(String message, BlockException e){
return "blockException";
}
public String fallback(String message, Throwable e){
return "throwable";
}
使用类
@SentinelResource(
value = "message2",
blockHandlerClass = OrderServiceImplBlockHandler.class,
blockHandler = "blockHandler",
fallbackClass = OrderServiceImplFallback.class,
fallback = "fallback"
)
public String message2(String message) {
return message;
}
public class OrderServiceImplBlockHandler {
public static String blockHandler(String message, BlockException e){
return "blockException";
}
}
public class OrderServiceImplFallback {
public static String fallback(String message, Throwable e){
return "throwable";
}
}
规则持久化
DataSource扩展常见的实现方式有:
- 拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是RDBMS、文件,甚至是VCS等。这样做的方式是简单,缺点是无法及时获取变更。
- 推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper
等配置中心。这种方式有更好的实时性和一致性保证。
Sentinel目前支持以下数据源扩展:
- Pull-based: 文件、Consul
- Push-based: ZooKeeper, Redis, Nacos, Apollo, etcd
Pull 模式
原理:
- 扩展写数据源(WritableDataSource), 客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件
等。 - pull 模式的数据源(如本地文件、RDBMS 等)一般是可写入的。使用时需要在客户端注册数据源:将对应的读数据源注册至对应的
RuleManager,将写数据源注册至 transport 的 WritableDataSourceRegistry 中。
导入依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-extension</artifactId>
</dependency>
添加文件
import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* FileDataSourceInit for : 自定义Sentinel存储文件数据源加载类
*/
public class FileDataSourceInit implements InitFunc {
@Override
public void init() throws Exception {
// TIPS: 如果你对这个路径不喜欢,可修改为你喜欢的路径
String ruleDir = System.getProperty("user.home") + "/sentinel/rules";
String flowRulePath = ruleDir + "/flow-rule.json";
String degradeRulePath = ruleDir + "/degrade-rule.json";
String systemRulePath = ruleDir + "/system-rule.json";
String authorityRulePath = ruleDir + "/authority-rule.json";
String hotParamFlowRulePath = ruleDir + "/param-flow-rule.json";
this.mkdirIfNotExits(ruleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(hotParamFlowRulePath);
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new FileRefreshableDataSource<>(
flowRulePath,
flowRuleListParser
);
// 将可读数据源注册至FlowRuleManager
// 这样当规则文件发生变化时,就会更新规则到内存
FlowRuleManager.register2Property(flowRuleRDS.getProperty());
WritableDataSource<List<FlowRule>> flowRuleWDS = new FileWritableDataSource<>(
flowRulePath,
this::encodeJson
);
// 将可写数据源注册至transport模块的WritableDataSourceRegistry中
// 这样收到控制台推送的规则时,Sentinel会先更新到内存,然后将规则写入到文件中
WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
// 降级规则
ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new FileRefreshableDataSource<>(
degradeRulePath,
degradeRuleListParser
);
DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
WritableDataSource<List<DegradeRule>> degradeRuleWDS = new FileWritableDataSource<>(
degradeRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
// 系统规则
ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new FileRefreshableDataSource<>(
systemRulePath,
systemRuleListParser
);
SystemRuleManager.register2Property(systemRuleRDS.getProperty());
WritableDataSource<List<SystemRule>> systemRuleWDS = new FileWritableDataSource<>(
systemRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
// 授权规则
ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new FileRefreshableDataSource<>(
flowRulePath,
authorityRuleListParser
);
AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new FileWritableDataSource<>(
authorityRulePath,
this::encodeJson
);
WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
// 热点参数规则
ReadableDataSource<String, List<ParamFlowRule>> hotParamFlowRuleRDS = new FileRefreshableDataSource<>(
hotParamFlowRulePath,
hotParamFlowRuleListParser
);
ParamFlowRuleManager.register2Property(hotParamFlowRuleRDS.getProperty());
WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new FileWritableDataSource<>(
hotParamFlowRulePath,
this::encodeJson
);
ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
}
/**
* 流控规则对象转换
*/
private Converter<String, List<FlowRule>> flowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<FlowRule>>() {
}
);
/**
* 降级规则对象转换
*/
private Converter<String, List<DegradeRule>> degradeRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<DegradeRule>>() {
}
);
/**
* 系统规则对象转换
*/
private Converter<String, List<SystemRule>> systemRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<SystemRule>>() {
}
);
/**
* 授权规则对象转换
*/
private Converter<String, List<AuthorityRule>> authorityRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<AuthorityRule>>() {
}
);
/**
* 热点规则对象转换
*/
private Converter<String, List<ParamFlowRule>> hotParamFlowRuleListParser = source -> JSON.parseObject(
source,
new TypeReference<List<ParamFlowRule>>() {
}
);
/**
* 创建目录
*
* @param filePath
*/
private void mkdirIfNotExits(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
file.mkdirs();
}
}
/**
* 创建文件
*
* @param filePath
* @throws IOException
*/
private void createFileIfNotExits(String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists()) {
file.createNewFile();
}
}
private <T> String encodeJson(T t) {
return JSON.toJSONString(t);
}
}
resource 目录下创建 resources/META-INF/services 目录并创建文件com.alibaba.csp.sentinel.init.InitFunc ,内容为:
pre.cg.config.FileDataSourceInit
Pull 优缺点
- 优点
- 简单,无任何依赖
- 没有额外依赖
- 缺点
- 不保证一致性(规则是使用FileRefreshableDataSource定时更新,会有延迟)
- 实时性不保证(规则是使用FileRefreshableDataSource定时更新)
- 拉取过于频繁也可能会有性能问题
- 由于文件存储于本地,容易丢失
Feign整合Sentinel
修改配置文件
#开启feign对sentinel支持
feign:
sentinel:
enabled: true
建立容错类
容错类,需要实现Fegin所在的接口,并实现接口的所有方法,一旦Fegin远程调用出现问题,就会进入当前类中同名方法,执行容错逻辑
@Service
public class ProductServiceFallback implements ProductFeignClient {
@Override
public Product findByPid(Long pid) {
Product product = new Product();
product.setId(250L)
.setName("fallback");
return product;
}
}
修改@FeignClient注解,添加容错类
@FeignClient(value = "service-product",qualifier = "service-product",fallback = ProductServiceFallback.class)
public interface ProductFeignClient {
@GetMapping("/product/{pid}")
@LoadBalanced
Product findByPid(@PathVariable("pid") Long pid);
}
容错类并获取报错信息(两种方法只能使用一个)
@FeignClient(value = "service-product",qualifier = "service-product"
,fallbackFactory = ProductServiceFallbackFactory.class)
public interface ProductFeignClient {
@GetMapping("/product/{pid}")
@LoadBalanced
Product findByPid(@PathVariable("pid") Long pid);
}
@Component
public class ProductServiceFallbackFactory implements FallbackFactory<ProductFeignClient> {
@Override
public ProductFeignClient create(Throwable throwable) {
return new ProductFeignClient() {
@Override
public Product findByPid(Long pid) {
Product product = new Product();
product.setId(250L)
.setName("fallback"+throwable.toString());
return product;
}
};
}
}