第三章 创建了生产者。以生产者为例集成sentinel
- 引入sentinel开发包
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
- 重写RequestOriginParser 设置请求来源(示例中已客户端ip作为来源)
package com.cloud.alibaba.config;
import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class MyRequestOriginParser implements RequestOriginParser {
@Override
public String parseOrigin(HttpServletRequest request) {
String ip = getIpAddr(request);
return ip;
}
private String getIpAddr(HttpServletRequest request) {
if (request == null) {
return "unknown";
}
String ip = request.getHeader("x-forwarded-for");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
}
}
- 覆盖 DefaultUrlBlockHandler 异常处理器处理器
package com.cloud.alibaba.config;
import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyUrlBlockHandler implements UrlBlockHandler {
public MyUrlBlockHandler() {
}
@Override
public void blocked(HttpServletRequest request, HttpServletResponse response, BlockException ex) throws IOException {
if (AuthorityException.isBlockException(ex)) {
Map<String,Object> map = new HashMap(2);
map.put("code",500);
map.put("msg","被sentinel拦截");
writer(request,response,map);
}
}
public static boolean writer(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Map map ) {
PrintWriter writer = null;
String originHeader = httpServletRequest.getHeader("Origin");
httpServletResponse.setHeader("Access-Control-Allow-Origin", originHeader);
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.addHeader("Access-Control-Allow-Credentials", "true");
httpServletResponse.addHeader("Vary", "Origin");
httpServletResponse.setContentType("application/json; charset=utf-8");
try {
JSONObject jsonObject = (JSONObject) JSON.toJSON(map);
writer = httpServletResponse.getWriter();
writer.append(jsonObject.toString());
} catch (IOException e) {
System.out.println("response error" + e);
} finally {
if (writer != null) {
writer.close();
}
}
return false;
}
}
- 将异常处理器添加至WebCallbackManager中
package com.cloud.alibaba.config;
import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SentinelConfig {
@Bean
public MyUrlBlockHandler webCallbackManager() {
MyUrlBlockHandler myUrlBlockHandler = new MyUrlBlockHandler();
WebCallbackManager.setUrlBlockHandler(myUrlBlockHandler);
return myUrlBlockHandler;
}
}
- bootstrap.yml 增加sentinel 配置
spring:
cloud:
sentinel:
eager: true
transport:
dashboard: 101.37.152.195:8858
port: 8719
datasource:
ds1:
nacos:
server-addr: 101.37.152.195:8848
dataId: consumer-example-flow
groupId: DEFAULT_GROUP
data-type: json
rule-type: flow
- nacos 增加sentinel配置
Data ID: consumer-example-flow
Group: DEFAULT_GROUP
配置格式: JSON
配置内容:
[
{
"resource": "/getUserNameTwo",
"limitApp": "192.168.1.25",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
},
{
"resource": "/getUserName",
"limitApp": "default",
"grade": 1,
"count": 10,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
- /getUserNameTwo 接口针对 ip 192.168.1.25 的来源进行限流 QPS 为1
- /getUserName 接口 针对所由的来源进行限流 QPS 为10
- 启动项目使用postman 等工具测试接口
- 在正式的场景中。我们可以把可疑的ip放入缓存中。然后将来源设置为正常状态和异常状态。在自定义来源方法中如果请求来源是缓存中的可疑ip。将来源设置为异常状态。黑名单规则里的来源应用设置为异常状态
限流规则
[
{
"resource": "/swagger-ui.html", // 资源名称
"limitApp": "192.168.1.25", // 来源应用 default 不区分调用者
"grade": 1, // 阀值类型,0表示线程数,1表示QPS
"count": 1, // 单机阀值
"strategy": 0, // 流控模式,0表示直接,1表示关联,2表示链路
"controlBehavior": 0, // 流控效果,0表示快速失败,1表示Warm UP,2表示排队等待
"clusterMode": false // 是否集群
}
]
// 192.168.1.25来源的请求 qps 为 1(每秒的查询数1次)
降级规则
[
{
"resource": "/swagger-ui.html", // 资源名称
"count": 3, // 最大Rt(ms)(平均响应时间 ) 比例阀值 异常数
"grade": 0, // 熔断策略 0 RT 1 异常比例 2 异常数
"timeWindow": 10, // 熔断时长S 10秒后回复
}
]
// 一秒内进来n个请求,平均响应时长均超过rt。对此接口进行熔断降级。10秒后恢复
热点规则
黑白名单
[
{
"resource": "/swagger-ui.html", // 资源名称
"limitApp": "192.168.1.25,127.0.0.1", // 对应的黑名单/白名单,不同 origin 用 , 分隔,如 appA,appB
"strategy": 1 // 限流模式 0 白名单 1 黑名单
}
]
// 192.168.1.25 和 127.0.0.1 的来源禁止请求