【实际开发10】- 远程调用 ( Feign )

目录

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"
    }
]

【实际开发10】- 远程调用 ( Feign )_第1张图片

 


1. 单一数据查询 , 可直接用 Feign单一查询接口


2. List 数据查询 , 需进行 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 亿)

【实际开发10】- 远程调用 ( Feign )_第2张图片

 


3. stream() : 从List<对象> , 取出 id 和 name 组成 map 集合

     Map collect = list
         .stream()
         .collect(Collectors.toMap(p -> p.getId() ,  p -> p.getName()));

Iot-emergency 应急管理

原始:

    /**
     * 资产结构数据处理
     * @return Map
     */
    private Map iotStructureMap(){
        IotStructureDTO iotStructureDTO = new IotStructureDTO();
        IotUser user = SecurityUtils.getUser();
        if (user != null) {
            iotStructureDTO.setTenantId(user.getTenantId());
        }
        HashMap structureMap = new HashMap<>(128);
        R> 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
     */
    private Map machineDataMap(){
        MachineData machineData = new MachineData();
        machineData.setDelFlag(1);
        R> 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 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 结果集 数据转换


1. 单查 feign API , 获取某模块数据 : map 容器化


2. 单查 feign API , 获取某模块数据 : cache 缓存 + map 容器化 ( 推荐 )


1. 对 feign API 数据源 作缓存 ( 主业务逻辑不做 )

例如 :

涉及 设备 status、摄像头 status 实时感知问题,不适合作较长时间的缓存,

此处只适合作时效性较短的缓存(过期时间 < 10 min)

@Override
@Transactional(rollbackFor = Exception.class)
public List selectListByCondition(IotAnchorDTO iotAnchorDTO) throws Exception {
    List iotAnchorVOList = iotAnchorMapper.selectListByCondition(iotAnchorDTO);
    if (null == iotAnchorVOList || iotAnchorVOList.isEmpty()) {
        return iotAnchorVOList;
    }
    try {
        // 设备数据信息 - 构建容器:machineMap
        Map machineMap = machineMap();
        // 摄像头数据信息 - 构建容器:cameraMap
        Map 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 r = remoteTenantService.isLimit(userId);
        if (0 != r.getCode()) {
            return R.failed("调用用户服务时发生异常");
        }


2. Iot 资产结构模块 - ( 单查 )

    /**
     * 根据锚点id 查询锚点关联资产结构信息
     * @param structureId 锚点id
     * @return IotAnchorVO
     */
    @Override
    public List selectIotAnchorInfoByStructureId(Integer structureId) throws Exception {
        List 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 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 structureIdList = iotStructureServiceImpl.getStructureIdList(iotStructureDTO);
        // 将idlist转换(封装进去)为锚点信息的入参对象
        IotAnchorDTO iotAnchorDTO = new IotAnchorDTO();
        iotAnchorDTO.setStructureIdList(structureIdList);
        List 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 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
     * @return Map
     */
    private Map machineMap(){
        // 容器构建
        HashMap machineMap = new HashMap<>(128);
        // 获取普通设备在线状态
        MachineData machineData = new MachineData();
        /*List structureIdList = new ArrayList<>();
        structureIdList.add(0, structureId);
        // 获取与资产结构idList关联的设备
        machineData.setStructureIdList(structureIdList);
        // 启用禁用【设备模块的机制】
        machineData.setDelFlag(1);*/
        R> onlineListFegin = remoteMachineDataService.getOnlineListFegin(machineData, com.linksame.iot.common.core.constant.SecurityConstants.FROM_IN);
        // 构建容器:machineMap
        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
     * @return Map
     */
    private Map cameraMap(){
        // 容器构建
        HashMap cameraMap = new HashMap<>(128);

        // 摄像头数据信息
        R> channelList = remoteWvpService.getChannelList("","");
        // 构建容器:cameraMap
        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;
    }


4. Feign 提示 / 异常


1. GET 请求以 @Requestbody 传参导致的异常 ( @R~body 用 POST )

你可能感兴趣的:(Java,篇,java,开发语言,经验分享)