目录
1. Feign 调用注意事项 - ★★★
1. 【原则】: 禁止遍历 - 多次跨服务调用接口 ( 提需求 : idList )
1. 单一数据查询 , 可直接用 Feign单一查询接口
2. List数据查询 , 需进行 Feign 数据转换 , 禁止遍历 Feign
3. stream() : 从List<对象> , 取出 id 和 name 组成 map 集合
4. for 循环 : 从List<对象> , 取出 id 和 Oject 组成 map 集合
2. 【原则】: 避免跨服务调用阻塞流程
1. 减少 跨服务 校验 ( 删除 - 资源占用 )
2. List 结果集 数据转换
1. 单查 feign API , 获取某模块数据 : map 容器化
2. 单查 feign API , 获取某模块数据 : cache 缓存 + map 容器化 ( 推荐 )
1. 对 feign API 数据源 作缓存 ( 主业务逻辑不做 )
2. 对全 业务 作缓存
3. 单查 feign API , 获取某模块数据 : 预热 + cache 缓存 + map 容器化
3. Feign 调用业务情景
1. CMP 配额模块
2. Iot 资产结构模块 - ( 单查 )
3. Iot 锚点数据转换 machineName / productType ( 业务内处理 × )
4. Iot 锚点转换 machine | camera ( 容器构建 map )
4. Feign 提示 / 异常
1. GET 请求以 @Requestbody 传参导致的异常 ( @R~body 用 POST )
1. Feign 调用注意事项 - ★★★
1. 【原则】: 禁止遍历 - 多次跨服务调用接口 ( 提需求 : idList )
路由 filter 过滤
限制 1 s 执行多少次 feign 调用
[
{
"args": {
"key-resolver": "#{@remoteAddrKeyResolver}" ,
"redis-rate-limiter.burstCapacity": "20" ,
"redis-rate-limiter.replenishRate": "10"
} ,
"name": "RequestRateLimiter"
} ,
{
"args": {
"name": "default" ,
"fallbackUri": "forward:/fallback"
} ,
"name": "Hystrix"
}
]
1. 单一数据查询 , 可直接用 Feign单一查询接口
2. List<Object>
数据查询 , 需进行 Feign 数据转换 , 禁止遍历 Feign
但HashMap占内存比较大 , 不少人都更关注于HashMap所占的内存怎么计算。
运行了一下 , 没找到HashMap的极限容量 , 但就发现HashMap很耗内存 , 一下子就OutOfMemoryError了。
Integer a = 1;
long start = 0;
long end = 0;
// 先垃圾回收
System.gc();
start = Runtime.getRuntime().freeMemory();
HashMap map = new HashMap();
for (int i = 0; i < 1000000; i++) {
map.put(i , a);
}
// 快要计算的时 , 再清理一次
System.gc();
end = Runtime.getRuntime().freeMemory();
System.out.println("一个HashMap对象占内存:" + (end - start));
当添加2000000个item的时候 , 就内存溢出了。
1000000个item的HashMap就占内存接近60M了~~夸张
map - key 上限
: 2^30 = 1 073 741 824 (10.7 亿)
3. stream() : 从List<对象>
, 取出 id 和 name 组成 map 集合
Map<String , String> collect = list .stream() .collect(Collectors.toMap(p -> p.getId() , p -> p.getName()));
Iot-emergency 应急管理
原始:
/**
* 资产结构数据处理
* @return Map<Integer , String>
*/
private Map<Integer , String> iotStructureMap(){
IotStructureDTO iotStructureDTO = new IotStructureDTO();
IotUser user = SecurityUtils.getUser();
if (user != null) {
iotStructureDTO.setTenantId(user.getTenantId());
}
HashMap<Integer , String> structureMap = new HashMap<>(128);
R<List<IotStructureVO>> listR = remoteIotStructureService.queryIotStructureList(iotStructureDTO);
if( 0 == listR.getCode() && !listR.getData().isEmpty()){
for (IotStructureVO iotStructureVO : listR.getData()) {
structureMap.put(iotStructureVO.getId() , iotStructureVO.getAddressPath());
}
}
return structureMap;
}
stream() 流处理:
/**
* 设备数据处理
* @return Map<Integer , String>
*/
private Map<Integer , String> machineDataMap(){
MachineData machineData = new MachineData();
machineData.setDelFlag(1);
R<List<MachineData>> listR = remoteMachineDataService.feginList(machineData);
if(0 == listR.getCode() && null != listR.getData() && !listR.getData().isEmpty()){
return listR.getData()
.stream()
.collect(Collectors.toMap(MachineData::getMachineId , MachineData::getMachineName));
}
return new HashMap<>();
}
4. for 循环 : 从List<对象>
, 取出 id 和 Oject 组成 map 集合
2. 【原则】: 避免跨服务调用阻塞流程
示例:
if ("0".equals(iotAnchorDTO.getType())) {
// 修改设备锚点状态 + 同步设备挂载资产id
MachineData machineData = new MachineData();
machineData.setMachineId(Integer.parseInt(iotAnchorDTO.getMachineId()));
machineData.setStructureId(iotAnchorDTO.getStructureId());
R<Boolean> update = remoteMachineDataService.update(machineData);
if (0 == update.getCode()) {
return Boolean.TRUE;
} else {
log.info("设备锚点状态同步异常");
/* 新增拦截提示:"throw new IotAssetStructureException("remoteMachineDataService.update()-设备锚点资产结构ID同步异常")" */
}
}
return Boolean.TRUE;
1. 减少 跨服务 校验 ( 删除 - 资源占用 )
2. List<xxxVO>
结果集 数据转换
1. 单查 feign API , 获取某模块数据 : map 容器化
2. 单查 feign API , 获取某模块数据 : cache 缓存 + map 容器化 ( 推荐 )
1. 对 feign API 数据源 作缓存 ( 主业务逻辑不做 )
例如 :
涉及 设备 status、摄像头 status 实时感知问题,不适合作较长时间的缓存,
此处只适合作时效性较短的缓存(过期时间 < 10 min)
@Override
@Transactional(rollbackFor = Exception.class)
public List<IotAnchorVO> selectListByCondition(IotAnchorDTO iotAnchorDTO) throws Exception {
List<IotAnchorVO> iotAnchorVOList = iotAnchorMapper.selectListByCondition(iotAnchorDTO);
if (null == iotAnchorVOList || iotAnchorVOList.isEmpty()) {
return iotAnchorVOList;
}
try {
// 设备数据信息 - 构建容器:machineMap<machineId,MachineData>
Map<Integer, MachineData> machineMap = machineMap();
// 摄像头数据信息 - 构建容器:cameraMap<machineId,DeviceChannel>
Map<String, DeviceChannel> cameraMap = cameraMap();
// 结果集数据处理
for (IotAnchorVO iotAnchorVO : iotAnchorVOList) {
// path 路径转换(AddressPath)
IotStructure iotStructure = iotStructureMapper.selectById(iotAnchorVO.getStructureId());
if (null != iotStructure) {
if (iotStructure.getLevel() < 2) {
iotAnchorVO.setAddressPath(iotStructure.getName());
} else {
String s = iotStructureServiceImpl.pathChanges(iotStructure.getPath(), iotStructure.getTenantId());
iotAnchorVO.setAddressPath(s + "/" + iotStructure.getName());
}
}
// 普通设备数据转换
if ("0".equals(iotAnchorVO.getType())) {
// 添加设备默认状态:1-不在线
iotAnchorVO.setMachineStatus("1");
// 转换设备名称等设备相关基础信息
if(null != iotAnchorVO.getMachineId()){
log.info("============iotAnchorVO.getMachineId():null===========");
continue;
}
MachineData mac = machineMap.get(Integer.parseInt(iotAnchorVO.getMachineId()));
if (null != mac) {
if (null != mac.getMachineName()) {
iotAnchorVO.setMachineName(mac.getMachineName());
}
// 设备状态:0-在线 1-不在线
if (null != mac.getOnlineFlag()) {
iotAnchorVO.setMachineStatus(mac.getOnlineFlag().toString());
}
if (null != mac.getTypeId()) {
iotAnchorVO.setProTypeId(mac.getTypeId());
}
}
// 摄像头数据转换
} else if ("1".equals(iotAnchorVO.getType())) {
// 添加摄像头默认状态:1-不在线
iotAnchorVO.setMachineStatus("1");
// 转换摄像头名称等设备相关基础信息
DeviceChannel deviceChannel = cameraMap.get(iotAnchorVO.getMachineId());
if (null != deviceChannel) {
if (null != deviceChannel.getName()) {
if(StringUtils.isNotBlank(deviceChannel.getRemark())){
iotAnchorVO.setMachineName(deviceChannel.getRemark() +"(" + deviceChannel.getName() +")" );
}else {
iotAnchorVO.setMachineName(deviceChannel.getName());
}
}else {
if(StringUtils.isNotBlank(deviceChannel.getRemark())){
iotAnchorVO.setMachineName(deviceChannel.getRemark());
}
}
// 摄像头状态:0-不在线 1-在线 【警告:这里状态需要统一转换】
if ("1".equals(String.valueOf(deviceChannel.getOnline()))) {
iotAnchorVO.setMachineStatus("0");
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
log.info("设备/摄像头数据转换数异常...");
}
return iotAnchorVOList;
}
2. 对全 业务 作缓存
3. 单查 feign API , 获取某模块数据 : 预热 + cache 缓存 + map 容器化
3. Feign 调用业务情景
1. CMP 配额模块
R<String> r = remoteTenantService.isLimit(userId);
if (0 != r.getCode()) {
return R.failed("调用用户服务时发生异常");
}
2. Iot 资产结构模块 - ( 单查 )
/**
* 根据锚点id 查询锚点关联资产结构信息
* @param structureId 锚点id
* @return IotAnchorVO
*/
@Override
public List<IotAnchorVO> selectIotAnchorInfoByStructureId(Integer structureId) throws Exception {
List<IotAnchorVO> iotAnchorVOS = iotAnchorMapper.selectIotAnchorInfoByStructureId(structureId);
// 设备id转换name
for(IotAnchorVO info:iotAnchorVOS){
Integer machineId = info.getMachineId();
// com.hzcloud.iot.common.core.constant.SecurityConstants.FROM_IN ----??
String fromIn = SecurityConstants.FROM_IN;
try {
R<MachineData> one = remoteMachineDataService.getOne(machineId , com.hzcloud.iot.common.core.constant.SecurityConstants.FROM_IN);
// 如果远程调用成功:转换MachineName信息
if (null != one) {
String machineName = one.getData().getMachineName();
info.setMachineName(machineName);
} else {
info.setMachineName("--");
log.info("remoteMachineDataService.getOne()数据转换异常");
}
} catch (Exception e) {
throw new Exception("remoteMachineDataService.getOne()数据转换异常");
}
}
return iotAnchorVOS;
}
3. Iot 锚点数据转换 machineName / productType ( 业务内处理 × )
/** ---------------------------通过资产结构id自己及子集的设备锚点信息-20.07.22------------------------------------*/
IotStructureDTO iotStructureDTO = new IotStructureDTO();
iotStructureDTO.setId(structureId);
List<Integer> structureIdList = iotStructureServiceImpl.getStructureIdList(iotStructureDTO);
// 将idlist转换(封装进去)为锚点信息的入参对象
IotAnchorDTO iotAnchorDTO = new IotAnchorDTO();
iotAnchorDTO.setStructureIdList(structureIdList);
List<IotAnchorVO> iotAnchorVOList = iotAnchorMapper.selectTrdListByCondition(iotAnchorDTO);
for (IotAnchorVO iotAnchorVO : iotAnchorVOList) {
if(null != iotAnchorVO.getMachineId()){
/*String str = iotAnchorService.changeMachineName(iotAnchorVO.getMachineId());
iotAnchorVO.setMachineName(str);*/
/** --------------------警告:后期更换idLIst查询接口 , 对比 MachineId , 进行数据转换-----------------------*/
try {
R<MachineData> one = remoteMachineDataService.getOne(iotAnchorVO.getMachineId() , com.hzcloud.iot.common.core.constant.SecurityConstants.FROM_IN);
// 如果远程调用成功:返回MachineName信息
if (null != one.getData().getMachineName()) {
iotAnchorVO.setMachineName(one.getData().getMachineName());
}
if (null != one.getData().getTypeId()){
iotAnchorVO.setProTypeId(one.getData().getTypeId());
}
} catch (Exception e) {
// throw new Exception("remoteMachineDataService.getOne()数据转换异常");
log.info("remoteMachineDataService.getOne()数据转换异常");
}
}
}
iotTrdPreviewVO.setMachineList(iotAnchorVOList);
return iotTrdPreviewVO;
}
4. Iot 锚点转换 machine | camera ( 容器构建 map )
/**
* 构建容器:machineMap<machineId,MachineData>
* @return Map<Integer, MachineData>
*/
private Map<Integer, MachineData> machineMap(){
// 容器构建
HashMap<Integer, MachineData> machineMap = new HashMap<>(128);
// 获取普通设备在线状态
MachineData machineData = new MachineData();
/*List<Integer> structureIdList = new ArrayList<>();
structureIdList.add(0, structureId);
// 获取与资产结构idList关联的设备
machineData.setStructureIdList(structureIdList);
// 启用禁用【设备模块的机制】
machineData.setDelFlag(1);*/
R<List<MachineData>> onlineListFegin = remoteMachineDataService.getOnlineListFegin(machineData, com.linksame.iot.common.core.constant.SecurityConstants.FROM_IN);
// 构建容器:machineMap<machineId,MachineData>
if (0 == onlineListFegin.getCode() && !onlineListFegin.getData().isEmpty()) {
for (MachineData datum : onlineListFegin.getData()) {
if (null != datum.getMachineId()) {
machineMap.put(datum.getMachineId(), datum);
}
}
} else {
log.info("remoteMachineDataService.getOnlineListFegin().getCode = 1,数据转换异常");
}
return machineMap;
}
/**
* 构建容器:cameraMap<machineId,DeviceChannel>
* @return Map<String, DeviceChannel>
*/
private Map<String, DeviceChannel> cameraMap(){
// 容器构建
HashMap<String, DeviceChannel> cameraMap = new HashMap<>(128);
// 摄像头数据信息
R<List<DeviceChannel>> channelList = remoteWvpService.getChannelList("","");
// 构建容器:cameraMap<machineId,DeviceChannel>
if(0 == channelList.getCode() && !channelList.getData().isEmpty()){
for (DeviceChannel deviceChannel : channelList.getData()) {
if (StringUtils.isNotBlank(deviceChannel.getId())) {
cameraMap.put(deviceChannel.getId(), deviceChannel);
}
}
}else{
log.info("remoteWvpService.getChannelList():数据转换异常");
}
return cameraMap;
}