alibaba / alibaba/Sentinel

[BUG] 热点参数集群限流,参数为非基本类型时,server 侧decode时data会是null,导致 NPE

Open
#1,906 4 comments 0 reactions 0 assignees View on GitHub
area/cluster-flow good first issue kind/bug
Dominant language
Java
Stars
23.1k
Forks
8.1k
PR merge metrics
No merged PRs in 30d

Description

## Issue Description

热点参数集群限流,如果参数是一个对象,因为netty在encode时会过滤掉,在tokenserver那解码时data会是null,导致空指针

### Describe what happened (or what feature you want)
我在使用@sentinelresource注解时,使用了对象为参数,测试热点集群限流,希望可以总体阈值生效,但是metric日志总是不生效

### Describe what you expected to happen

### How to reproduce it (as minimally and precisely as possible)

![image](https://user-images.githubusercontent.com/33927898/102344597-181cef00-3fd7-11eb-8c0c-a28d348c1c44.png)

### Tell us your environment
jdk1.8
sentinel1.8

### Anything else we need to know?
编码时跳过参数
```java
int calculateParamTransportSize(Object value) {
if (value == null) {
return 0;
}
// Layout for primitives: |type flag(1)|value|
// size = original size + type flag (1)
if (value instanceof Integer || int.class.isInstance(value)) {
return 5;
} else if (value instanceof String) {
// Layout for string: |type flag(1)|length(4)|string content|
String tmpValue = (String) value;
byte[] tmpChars = tmpValue.getBytes();
return 1 + 4 + tmpChars.length;
} else if (boolean.class.isInstance(value) || value instanceof Boolean) {
return 2;
} else if (long.class.isInstance(value) || value instanceof Long) {
return 9;
} else if (double.class.isInstance(value) || value instanceof Double) {
return 9;
} else if (float.class.isInstance(value) || value instanceof Float) {
return 5;
} else if (byte.class.isInstance(value) || value instanceof Byte) {
return 2;
} else if (short.class.isInstance(value) || value instanceof Short) {
return 3;
} else {
// Ignore unexpected type.
return 0;
}
```
解码时
```java
public class ParamFlowRequestDataDecoder implements EntityDecoder {

@Override
public ParamFlowRequestData decode(ByteBuf source) {
if (source.readableBytes() >= 16) {
ParamFlowRequestData requestData = new ParamFlowRequestData()
.setFlowId(source.readLong())
.setCount(source.readInt());

int amount = source.readInt();
//因为amount为0,所以跳出判断,返回一个null
if (amount > 0) {
List params = new ArrayList<>(amount);
for (int i = 0; i < amount; i++) {
decodeParam(source, params);
}

requestData.setParams(params);
return requestData;
}
}
return null;
}
```

```java
public class ParamFlowRequestProcessor implements RequestProcessor {

@Override
public ClusterResponse processRequest(ClusterRequest request) {
TokenService tokenService = TokenServiceProvider.getService();
//data为null,报空指针,导致我的令牌请求失败
long flowId = request.getData().getFlowId();
int count = request.getData().getCount();
Collection args = request.getData().getParams();

TokenResult result = tokenService.requestParamToken(flowId, count, args);
return toResponse(result, request);
}
```

Contributor guide

Open the contributing guide

Research direction

Start with ParamFlowRequestDataDecoder.decode and ParamFlowRequestProcessor.processRequest, then trace how calculateParamTransportSize handles non-primitive parameters. Verify that a request containing an object parameter is decoded into usable request data and reaches tokenService.requestParamToken without a null-data failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend, distributed-systems
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.