使用场景:
app--(http)-->netty--(tcp)-->device
场景说明:
当app需要控制一个设备的开关,不仅物理设备需要有响应,同时需要给app响应开关的控制结果,这种情况下,如果在netty通过tcp发送给device的时候得到响应,那么就会是一个新的线程处理这个响应,跟之前发送的线程无关,这样的话可能不知道怎么响应给app
解决方案
可以使用juc中的CountDownLatch解决
代码实现
同步请求类
package com.ywb.syc2asyc;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @Author ywb
* @Date 2023/10/15 14:47
*/
public class ScyRqe {
/**
* juc中的阻塞工具
*/
private final CountDownLatch countDownLatch = new CountDownLatch(1);
/**
* device 响应体
*/
private Object responseBoyd;
/**
* 当前ScyRqe对于的key
*/
private String key;
public ScyRqe await(int timeOut, TimeUnit timeUnit) {
try {
//在这里阻塞等待
boolean waiteNoTimeOut = countDownLatch.await(timeOut, timeUnit);
//超时了,或者说有响应结果了(这里的其实还可以直接将自己从map中清理)
ScyRqe scyRqe = SycReqManager.obtainScyRqe(key);
//未超时就返回自己 , 否则返回空
return waiteNoTimeOut ? scyRqe : null;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public void putResponseBody(Object responseBoyd) {
this.responseBoyd = responseBoyd;
//打断阻塞
countDownLatch.countDown();
}
public Object getResponseBoyd() {
return responseBoyd;
}
}
同步请求类管理器
package com.ywb.syc2asyc;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Author ywb
* @Date 2023/10/15 14:46
*/
public class SycReqManager {
/**
* 管理ScyRqe
*/
private final static ConcurrentHashMap<String, ScyRqe> ScyRqeList = new ConcurrentHashMap<>();
/**
* 存进去
*/
public static ScyRqe contributeScyRqe(String key, ScyRqe scyRqe) {
return ScyRqeList.put(key, scyRqe);
}
/**
* 取出来
*/
public static ScyRqe obtainScyRqe(String key) {
return ScyRqeList.remove(key);
}
}
模拟netty请求响应
package com.ywb.syc2asyc;
import java.util.concurrent.TimeUnit;
/**
* @Author ywb
* @Date 2023/10/15 14:56
* 模拟请求响应
*/
public class ImitateRequest {
/**
* 模拟从netty发送数据给device
*
* @return 响应给app的数据
*/
public Object request() {
final String key = "请求id 或者 (设备类型+设备id+控制命令) 等";
//创建同步请求
ScyRqe scyRqe = new ScyRqe();
//存入管理器中,会返回同一个key的value,如果不为空,代表刚才在超时时间内请求过了,那就不管,否则存进去并且发送给device
ScyRqe scyRqeOld = SycReqManager.contributeScyRqe(key, scyRqe);
if (scyRqeOld == null) {
//发送消息 send2Device();
//阻塞等待结果
ScyRqe scyRqeResponse = scyRqe.await(1, TimeUnit.SECONDS);
if (scyRqeResponse == null) {
//超时
return "超时";
}
return scyRqeResponse.getResponseBoyd();
}
return "短时间内请求过了,不能多次请求";
}
/**
* 模拟从device响应结果给netty
*/
public void response(Object deviceResponseBody) {
//解析 deviceResponseBody 获取到 key
final String key = "请求id 或者 (设备类型+设备id+控制命令) 等";
//从管理器中获取同步请求
ScyRqe scyRqe = SycReqManager.obtainScyRqe(key);
//将结果放进去,这时候阻塞就会被打断,上面的scyRqe.await(1, TimeUnit.SECONDS)被放行
scyRqe.putResponseBody(deviceResponseBody);
}
}