【Spring Cloud】新闻头条微服务项目:文章内容安全审核(新增DFA+OCR过滤敏感词需求)

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 对文章审核新增加了需求----敏感词过滤,介绍了基于NFA的文本敏感词过滤及OCR提取图片文字的原理及实现,最后将文章审核代码做了合并。

一:需求新增

1.需求分析

       前面我们的想法是调用腾讯云的内容安全对文章的文本信息及图片进行安全检测,通过则将文章发布。但是这时候有个缺点,就是审核不能过滤一些敏感词,比如针孔摄像头、高额贷款、论文代写等,我们这时候就需要自己维护一套敏感词系统用来对文章进行检测。对于这套敏感词系统,我们可以在管理端进行删除增添的操作。

image.gif编辑

       除了要对文本进行敏感词过滤之外,由于图片中可能还会存在敏感词,所以我们也需要对图片中的文字进行检测,使用到的技术是OCR。

2.技术选型

(1)文本

方案 说明
数据库模糊查询 效率太低
String.indexOf("")查找 数据库量大的话也是比较慢
全文检索 分词再匹配
DFA算法 确定有穷自动机(一种数据结构)

在文字过滤系统中,为了能够应付较高的并发,需要尽量减少计算。采用DFA算法基本没有什么计算,基本是状态转移。所以选用最后一种方案。

(2)图片

方案 说明
百度OCR 收费
Tesseract-OCR Google维护的开源OCR引擎,支持Java,Python等语言调用
Tess4J 封装了Tesseract-OCR ,支持Java调用

Tess4J封装了Tesseract-OCR,Java调用起来方便,因此选用此方案。

3.DFA

(1)原理分析

DFA全称为:Deterministic Finite Automaton,即确定有穷自动机。

其特征为:有一个有限状态集合和一些从一个状态通向另一个状态的边,每条边上标记有一个符号,其中一个状态是初态,某些状态是终态。但不同于不确定的有限自动机,DFA中不会有从同一状态出发的两条边标志有相同的符号。image.gif编辑

简单点说就是,它是是通过event和当前的state得到下一个state,即event+state=nextstate。理解为系统中有多个节点,通过传递进入的event,来确定走哪个路由至另一个节点,而节点是有限的。

存储:一次性的把所有的敏感词存储到了多个map中,就是下图表示这种结构

敏感词:冰毒、大麻、大坏蛋

image.gif编辑        其处理过程为先检索文章中每一个字符,然后查看该字符是否包含在敏感词库中,假如包含在敏感词库中则查看该字符在敏感词库中isEnd是否为0,假如不为0则继续检索下一个字符,然后继续查看该字符是否存在于敏感词库中,假如存在则查看isEnd是否为1,假如为1则说明是敏感词。举个例子,假如有这样一句话:“我不卖冰毒”,这时候就会先检索“我”字,发现不在敏感词库,然后继续检索下一个,直至到“冰”,这时候“冰”在敏感词库中,但是isEnd为0,继续检索,“毒”在敏感词库且isEnd为1,这时候检测到敏感词“冰毒”。

(2)代码实现

①创建敏感词表:

image.gif编辑

实体类:

package com.my.model.wemedia.pojos;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
 * <p>
 * 敏感词信息表
 * </p>
 *
 * @author itheima
 */
@Data
@TableName("wm_sensitive")
public class WmSensitive implements Serializable {
    private static final long serialVersionUID = 1L;
    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.ASSIGN_ID)
    private Integer id;
    /**
     * 敏感词
     */
    @TableField("sensitives")
    private String sensitives;
    /**
     * 创建时间
     */
    @TableField("created_time")
    private Date createdTime;
}

image.gif

②创建对应Mapper接口

③导入工具类

package com.my.utils.common;
import java.util.*;
public class SensitiveWordUtil {
    public static Map<String, Object> dictionaryMap = new HashMap<>();
    /**
     * 生成关键词字典库
     * @param words
     * @return
     */
    public static void initMap(Collection<String> words) {
        if (words == null) {
            System.out.println("敏感词列表不能为空");
            return ;
        }
        // map初始长度words.size(),整个字典库的入口字数(小于words.size(),因为不同的词可能会有相同的首字)
        Map<String, Object> map = new HashMap<>(words.size());
        // 遍历过程中当前层次的数据
        Map<String, Object> curMap = null;
        Iterator<String> iterator = words.iterator();
        while (iterator.hasNext()) {
            String word = iterator.next();
            curMap = map;
            int len = word.length();
            for (int i =0; i < len; i++) {
                // 遍历每个词的字
                String key = String.valueOf(word.charAt(i));
                // 当前字在当前层是否存在, 不存在则新建, 当前层数据指向下一个节点, 继续判断是否存在数据
                Map<String, Object> wordMap = (Map<String, Object>) curMap.get(key);
                if (wordMap == null) {
                    // 每个节点存在两个数据: 下一个节点和isEnd(是否结束标志)
                    wordMap = new HashMap<>(2);
                    wordMap.put("isEnd", "0");
                    curMap.put(key, wordMap);
                }
                curMap = wordMap;
                // 如果当前字是词的最后一个字,则将isEnd标志置1
                if (i == len -1) {
                    curMap.put("isEnd", "1");
                }
            }
        }
        dictionaryMap = map;
    }
    /**
     * 搜索文本中某个文字是否匹配关键词
     * @param text
     * @param beginIndex
     * @return
     */
    private static int checkWord(String text, int beginIndex) {
        if (dictionaryMap == null) {
            throw new RuntimeException("字典不能为空");
        }
        boolean isEnd = false;
        int wordLength = 0;
        Map<String, Object> curMap = dictionaryMap;
        int len = text.length();
        // 从文本的第beginIndex开始匹配
        for (int i = beginIndex; i < len; i++) {
            String key = String.valueOf(text.charAt(i));
            // 获取当前key的下一个节点
            curMap = (Map<String, Object>) curMap.get(key);
            if (curMap == null) {
                break;
            } else {
                wordLength ++;
                if ("1".equals(curMap.get("isEnd"))) {
                    isEnd = true;
                }
            }
        }
        if (!isEnd) {
            wordLength = 0;
        }
        return wordLength;
    }
    /**
     * 获取匹配的关键词和命中次数
     * @param text
     * @return
     */
    public static Map<String, Integer> matchWords(String text) {
        Map<String, Integer> wordMap = new HashMap<>();
        int len = text.length();
        for (int i = 0; i < len; i++) {
            int wordLength = checkWord(text, i);
            if (wordLength > 0) {
                String word = text.substring(i, i + wordLength);
                // 添加关键词匹配次数
                if (wordMap.containsKey(word)) {
                    wordMap.put(word, wordMap.get(word) + 1);
                } else {
                    wordMap.put(word, 1);
                }
                i += wordLength - 1;
            }
        }
        return wordMap;
    }
/*     public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("法轮");
        list.add("法轮功");
        list.add("冰毒");
        initMap(list);
        String content="我是一个好人,并不会卖冰毒,也不操练法轮功,我真的不卖冰毒";
        Map<String, Integer> map = matchWords(content);
        System.out.println(map);
    } */
}

image.gif

4.OCR

(1)原理分析

OCR (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗、亮的模式确定其形状,然后用字符识别方法将形状翻译成计算机文字的过程。

(2)代码实现

①在tbug-headlines-common中导入如下依赖

<dependency>
    <groupId>net.sourceforge.tess4j</groupId>
    <artifactId>tess4j</artifactId>
    <version>4.1.1</version>
</dependency>

image.gif

②封装成工具类

package com.my.common.tess4j;
import lombok.Getter;
import lombok.Setter;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.awt.image.BufferedImage;
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "tess4j")
public class Tess4jClient {
    private String dataPath;
    private String language;
    public String doOCR(BufferedImage image) throws TesseractException {
        //创建Tesseract对象
        ITesseract tesseract = new Tesseract();
        //设置字体库路径
        tesseract.setDatapath(dataPath);
        //中文识别
        tesseract.setLanguage(language);
        //执行ocr识别
        String result = tesseract.doOCR(image);
        //替换回车和tal键  使结果为一行
        result = result.replaceAll("\\r|\\n", "-").replaceAll(" ", "");
        return result;
    }
}

image.gif

③在spring.factories配置中添加该类,完整如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.my.common.exception.ExceptionCatch,\
  com.my.common.swagger.SwaggerConfiguration,\
  com.my.common.swagger.Swagger2Configuration,\
  com.my.common.tencentcloud.TextDetection,\
  com.my.common.tencentcloud.ImageDetection,\
  com.my.common.tess4j.Tess4jClient,\

image.gif

④在tbug-headlines-wemedia中添加如下配置

tess4j:
  data-path: D:\headlinesPro\tessdata
  language: chi_sim

image.gif

注意文件路径要填自己的路径

二:完整审核流程

1.审核类

package com.my.wemedia.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.my.apis.article.IArticleClient;
import com.my.common.tencentcloud.ImageDetection;
import com.my.common.tencentcloud.TextDetection;
import com.my.common.tess4j.Tess4jClient;
import com.my.file.service.FileStorageService;
import com.my.model.article.dtos.ArticleDto;
import com.my.model.common.dtos.ResponseResult;
import com.my.model.wemedia.pojos.WmChannel;
import com.my.model.wemedia.pojos.WmNews;
import com.my.model.wemedia.pojos.WmSensitive;
import com.my.model.wemedia.pojos.WmUser;
import com.my.utils.common.SensitiveWordUtil;
import com.my.wemedia.mapper.WmSensitiveMapper;
import com.my.wemedia.service.WmAutoScanService;
import com.my.wemedia.service.WmChannelService;
import com.my.wemedia.service.WmNewsService;
import com.my.wemedia.service.WmUserService;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.stream.Collectors;
@Slf4j
@Service
@Transactional
public class WmAutoScanServiceImpl implements WmAutoScanService {
    @Autowired
    private WmNewsService wmNewsService;
    @Autowired
    private TextDetection textDetection;
    @Autowired
    private ImageDetection imageDetection;
    /**
     * 自动审核文章文本及图片
     * @param id
     */
    @Override
    @Async //表明这是一个异步方法
    public void AutoScanTextAndImage(Integer id) throws Exception {
        log.info("开始进行文章审核...");
        // Thread.sleep(300);    //休眠300毫秒,以保证能够获取到数据库中的数据
        WmNews wmNews = wmNewsService.getById(id);
        if(wmNews == null) {
            throw new RuntimeException("WmAutoScanServiceImpl-文章信息不存在");
        }
        if(wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())) {
            //1.提取文章文本及图片
            Map<String,Object> map = getTextAndImages(wmNews);
            //2.检测文本
            //2.1提取文本
            String content = ((StringBuilder) map.get("content")).toString();
            //2.2自管理敏感词过滤
            boolean SHandleResult = handleSensitiveScan(content,wmNews);
            if(!SHandleResult) return;
            //2.3调用腾讯云进行文本检测
            Boolean THandleResult = handleTextScan(content, wmNews);
            if(!THandleResult) return;
            //3.检测图片
            //3.1提取图片
            List<String> imageUrl = (List<String>) map.get("images");
            //3.2调用腾讯云对图片进行检测
            Boolean IHandleResult = handleImageScan(imageUrl, wmNews);
            if(!IHandleResult) return;
            //4,审核成功
            //4.1保存文章
            log.info("检测到文章无违规内容");
            ResponseResult responseResult = saveAppArticle(wmNews);
            if(!responseResult.getCode().equals(200)) {
                throw new RuntimeException("WmAutoScanServiceImpl-文章审核,保存文章失败");
            }
            //4.2回填article_id
            wmNews.setArticleId((Long) responseResult.getData());
            wmNews.setStatus(WmNews.Status.PUBLISHED.getCode());
            wmNews.setReason("审核成功");
            wmNewsService.updateById(wmNews);
        }
    }
    /**
     * 提取文章文本及图片信息
     * @param wmNews
     * @return
     */
    private Map<String,Object> getTextAndImages(WmNews wmNews) {
        //存储纯文本
        StringBuilder text = new StringBuilder();
        //存储图片链接
        List<String> imageUrl = new ArrayList<>();
        //1.从文章内容中提取文本及图片
        if(StringUtils.isNotBlank(wmNews.getContent())) {
            List<Map> maps = JSON.parseArray(wmNews.getContent(),Map.class);
            for (Map map : maps) {
                if(map.get("type").equals("text")) {
                    //文本
                    text.append(map.get("value"));
                }
                if(map.get("type").equals("image")) {
                    //图片
                    imageUrl.add((String) map.get("value"));
                }
            }
        }
        //2.从封面中提取图片
        if(StringUtils.isNotBlank(wmNews.getImages())) {
            for (String imageurl : wmNews.getImages().split(",")) {
                imageUrl.add(imageurl);
            }
        }
        //3.结果封装
        Map<String,Object> map = new HashMap<>();
        map.put("content",text);
        map.put("images",imageUrl);
        return map;
    }
    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;
    /**
     * 自管理敏感词过滤
     * @param content
     * @param wmNews
     * @return
     */
    private boolean handleSensitiveScan(String content,WmNews wmNews) {
        boolean flag = true;
        //1.获取所有的敏感词
        List<WmSensitive> wmSensitives = wmSensitiveMapper.selectList(Wrappers.<WmSensitive>lambdaQuery().select(WmSensitive::getSensitives));
        List<String> sensitiveList = wmSensitives.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
        //2.初始化敏感词库
        SensitiveWordUtil.initMap(sensitiveList);
        //3.检测文章是否包含敏感词
        Map<String, Integer> map = SensitiveWordUtil.matchWords(content);
        if(map.size() > 0) {
            log.info("当前文章存在敏感词:{}",map.keySet());
            flag = false;
            wmNews.setStatus(WmNews.Status.FAIL.getCode());
            wmNews.setReason("当前文章存在敏感词"+map.keySet());
            wmNewsService.updateById(wmNews);
        }
        return flag;
    }
    /**
     * 文本检测
     * @param content
     * @param wmNews
     * @return
     */
    private Boolean handleTextScan(String content,WmNews wmNews) throws TencentCloudSDKException {
        log.info("开始检测文章文本内容...");
        boolean flag = true;
        //1.文本为空
        if(StringUtils.isBlank(content) && StringUtils.isBlank(wmNews.getTitle())) {
            return true;
        }
        //2.调用腾讯云对文本进行检测
        JSONObject resultJson = textDetection.greenTextDetection(content + wmNews.getTitle());
        //审核不通过
        if(resultJson.get("Suggestion").equals("Block")) {
            log.warn("文章存在违规文本,审核不成功");
            flag = false;
            wmNews.setStatus(WmNews.Status.FAIL.getCode());
            wmNews.setReason("文章存在违规文本,审核不成功");
            wmNewsService.updateById(wmNews);
        }
        //自动审核不确定
        if(resultJson.get("Suggestion").equals("Review")) {
            log.info("文章存在不确定文本");
            flag = false;
            wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
            wmNews.setReason("文章存在不确定文本");
            wmNewsService.updateById(wmNews);
        }
        return flag;
    }
    @Autowired
    private FileStorageService fileStorageService;
    @Autowired
    private Tess4jClient tess4jClient;
    /**
     * 图片检测
     * @param list
     * @param wmNews
     * @return
     * @throws TencentCloudSDKException
     */
    private Boolean handleImageScan(List<String> list, WmNews wmNews) throws TencentCloudSDKException {
        log.info("开始检测文章图片内容...");
        boolean flag = true;
        //图片信息为空
        if(list.size() == 0) {
            return true;
        }
        //1.图片去重
        List<String> imageList = list.stream().distinct().collect(Collectors.toList());
        //2.调用腾讯云接口逐一检测
        for (String image : imageList) {
            try {
                //敏感词检测
                //1.下载minio图片文件
                byte[] bytes = fileStorageService.downLoadFile(image);
                //2.从byte[]转换为bufferedImage
                ByteArrayInputStream in = new ByteArrayInputStream(bytes);
                BufferedImage imageFile = ImageIO.read(in);
                //3.识别图片文字
                if(imageFile != null) {
                    String result = tess4jClient.doOCR(imageFile);
                    //4.审核图片是否包含敏感词
                    boolean isSensitive = handleSensitiveScan(result,wmNews);
                    if(!isSensitive) {
                        return false;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            //腾讯云图片安全检测
            JSONObject resultJson = imageDetection.greenImageDetection(image);
            //审核不通过
            if(resultJson.get("Suggestion").equals("Block")) {
                log.warn("文章存在违规图片,审核不成功");
                flag = false;
                wmNews.setStatus(WmNews.Status.FAIL.getCode());
                wmNews.setReason("文章存在违规图片,审核不成功");
                wmNewsService.updateById(wmNews);
            }
            //自动审核不确定
            if(resultJson.get("Suggestion").equals("Review")) {
                log.info("文章存在不确定图片");
                flag = false;
                wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
                wmNews.setReason("文章存在不确定图片");
                wmNewsService.updateById(wmNews);
            }
        }
        return flag;
    }
    @Autowired
    private WmChannelService wmChannelService;
    @Autowired
    private WmUserService wmUserService;
    @Resource
    private IArticleClient iArticleClient;
    /**
     * 保存App端文章数据
     * @param wmNews
     * @return
     */
    @Override
    public ResponseResult saveAppArticle(WmNews wmNews) {
        ArticleDto articleDto = new ArticleDto();
        //1.属性拷贝
        BeanUtils.copyProperties(wmNews,articleDto);
        //2.设置文章布局
        articleDto.setLayout(wmNews.getType());
        //3.设置频道信息
        WmChannel channel = wmChannelService.getById(wmNews.getChannelId());
        if(channel != null) {
            articleDto.setChannelName(channel.getName());
        }
        //4.设置作者信息
        articleDto.setAuthorId(wmNews.getUserId().longValue());
        WmUser wmUser = wmUserService.getById(wmNews.getUserId());
        if(wmUser != null) {
            articleDto.setAuthorName(wmUser.getName());
        }
        //5.设置文章id
        if(wmNews.getArticleId() != null) {
            articleDto.setId(wmNews.getArticleId());
        }
        //6.设置创建时间
        articleDto.setCreatedTime(new Date());
        //7.保存App端文章
        log.info("异步调用保存文章至App端");
        return iArticleClient.saveArticle(articleDto);
    }
}

image.gif

2.功能调用

在文章提交实现类中添加对自动审核方法的调用(见第五步)

/**
 * 提交文章
 * @param dto
 * @return
 */
@Override
public ResponseResult submitNews(WmNewsDto dto) {
    //1.参数校验
    if(dto == null || dto.getContent().length() == 0) {
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
    //2.保存或修改文章
    //2.1属性拷贝
    WmNews wmNews = new WmNews();
    BeanUtils.copyProperties(dto,wmNews);
    //2.2设置封面图片
    if(dto.getImages() != null && dto.getImages().size() != 0) {
        String images = StringUtils.join(dto.getImages(), ",");
        wmNews.setImages(images);
    }
    //2.3封面类型为自动
    if(dto.getType().equals(WemediaConstants.WM_NEWS_TYPE_AUTO)) {
        wmNews.setType(null);
    }
    saveOrUpdateWmNews(wmNews);
    //3.判断是否为草稿
    if(dto.getStatus().equals(WmNews.Status.NORMAL.getCode())) {
        //直接保存结束
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }
    //4.不是草稿
    //4.1保存文章图片素材与文章关系
    //4.1.1提取图片素材列表
    List<String> imagesList = getImagesList(dto);
    //4.1.2保存
    saveRelatedImages(imagesList,wmNews.getId(),WemediaConstants.WM_CONTENT_REFERENCE);
    //4.2保存封面图片和文章关系
    saveRelatedCover(dto,imagesList,wmNews);
    //5.审核文章(异步调用)
    wmAutoScanService.AutoScanTextAndImage(wmNews.getId());
    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

image.gif

下篇预告:实现文章定时发布

相关文章
|
4天前
|
安全 Java API
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)
22 0
第7章 Spring Security 的 REST API 与微服务安全(2024 最新版)(上)
|
4天前
|
Java API 微服务
【Spring Boot系列】通过OpenAPI规范构建微服务服务接口
【4月更文挑战第5天】通过OpenAPI接口构建Spring Boot服务RestAPI接口
23 0
|
6天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
13天前
|
监控 数据可视化 安全
智慧工地SaaS可视化平台源码,PC端+APP端,支持二开,项目使用,微服务+Java++vue+mysql
环境实时数据、动态监测报警,实时监控施工环境状态,有针对性地预防施工过程中的环境污染问题,打造文明生态施工,创造绿色的生态环境。
11 0
智慧工地SaaS可视化平台源码,PC端+APP端,支持二开,项目使用,微服务+Java++vue+mysql
|
15天前
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
|
15天前
|
SpringCloudAlibaba Java API
SpringCloud Alibaba微服务工程搭建(保姆级)
SpringCloud Alibaba微服务工程搭建(保姆级)
|
15天前
|
SpringCloudAlibaba Java Nacos
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
SpringCloud Alibaba微服务 -- Nacos使用以及注册中心和配置中心的应用(保姆级)
|
21天前
|
负载均衡 网络协议 Java
构建高效可扩展的微服务架构:利用Spring Cloud实现服务发现与负载均衡
本文将探讨如何利用Spring Cloud技术实现微服务架构中的服务发现与负载均衡,通过注册中心来管理服务的注册与发现,并通过负载均衡策略实现请求的分发,从而构建高效可扩展的微服务系统。
|
3天前
|
敏捷开发 监控 数据管理
构建高效微服务架构的五大关键策略
【4月更文挑战第20天】在当今软件开发领域,微服务架构已经成为一种流行的设计模式,它允许开发团队以灵活、可扩展的方式构建应用程序。本文将探讨构建高效微服务架构的五大关键策略,包括服务划分、通信机制、数据管理、安全性考虑以及监控与日志。这些策略对于确保系统的可靠性、可维护性和性能至关重要。
|
15天前
|
API 数据库 开发者
构建高效可靠的微服务架构:后端开发的新范式
【4月更文挑战第8天】 随着现代软件开发的复杂性日益增加,传统的单体应用架构面临着可扩展性、维护性和敏捷性的挑战。为了解决这些问题,微服务架构应运而生,并迅速成为后端开发领域的一股清流。本文将深入探讨微服务架构的设计原则、实施策略及其带来的优势与挑战,为后端开发者提供一种全新视角,以实现更加灵活、高效和稳定的系统构建。
18 0