您当前的位置:首页 > 电脑百科 > 程序开发 > 框架

基于DDD的微服务落地

时间:2023-03-08 11:51:26  来源:今日头条  作者:天之道居

DDD四层架构

DDD四层架构

对实时性要求高的强一致性业务场景,可采取分布式事务。分布式事务有性能代价,在设计时需要平衡考虑业务拆分、数据一致性、性能和实现的复杂度,尽量避免分布式事务的产生。

领域事件驱动的异步方式是分布式架构常用的设计方式,可以解决非实时性场景下的数据最终一致性问题。基于消息中间件的领域事件发布和订阅,可以很好的解耦微服务。通过削峰填谷,实现读写分离、减轻数据库实时访问压力,提高业务吞吐量和业务处理能力。

DDD设计思想和方法

DDD设计思想和方法

聚合根

聚合根包括聚合根属性、关联的实体和值对象以及自身的业务行为等。通过引用实体和值对象,协调聚合内的多个实体,在聚合根类方法中完成多实体的复杂业务逻辑。

充血模型:即领域模型模式。有自己的业务行为(方法),如下充血模型中提供getDuration、addHistoryApprovalInfo等方法。

贫血模型:即事务脚本模式。只有对象属性和setter/getter。


@Data
public class Leave {
    String id;
    Applicant applicant;
    Approver approver;
    LeaveType type;
    Status status;
    Date startTime;
    Date endTime;
    long duration;
    //审批领导的最大级别
    int leaderMaxLevel;
    ApprovalInfo currentApprovalInfo;
    List<ApprovalInfo> historyApprovalInfos;
 
    public long getDuration() {
        return endTime.getTime() - startTime.getTime();
    }
 
    public Leave addHistoryApprovalInfo(ApprovalInfo approvalInfo) {
        if (null == historyApprovalInfos)
            historyApprovalInfos = new ArrayList<>();
        this.historyApprovalInfos.add(approvalInfo);
        return this;
    }
 
    public Leave create(){
        this.setStatus(Status.APPROVING);
        this.setStartTime(new Date());
        return this;
    }
 
    public Leave agree(Approver nextApprover){
        this.setStatus(Status.APPROVING);
        this.setApprover(nextApprover);
        return this;
    }
 
    public Leave reject(Approver approver){
        this.setApprover(approver);
        this.setStatus(Status.REJECTED);
        this.setApprover(null);
        return this;
    }
 
    public Leave finish(){
        this.setApprover(null);
        this.setStatus(Status.APPROVED);
        this.setEndTime(new Date());
        this.setDuration(this.getEndTime().getTime() - this.getStartTime().getTime());
        return this;
    }
}

实体

实体有自己的属性和关联的值对象。

@Data
public class ApprovalInfo {
    String approvalInfoId;
    Approver approver;
    ApprovalType approvalType;
    String msg;
    long time;
}

值对象

public enum Status {
    APPROVING, APPROVED, REJECTED
}

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Approver {
    String personId;
    String personName;
    int level;
  
    public static Approver fromPerson(Person person){
        Approver approver = new Approver();
        approver.setPersonId(person.getPersonId());
        approver.setPersonName(person.getPersonName());
        approver.setLevel(person.getRoleLevel());
        return approver;
    }
}

领域对象

如果一个业务行为由多个实体对象参与完成,就将该业务逻辑放在领域服务中实现。

实体方法:完成单一实体自身的业务逻辑,是相对简单的原子业务逻辑。

领域服务:由多个实体组合的相对复杂的业务逻辑。

@Service
@Slf4j
public class LeaveDomAInService {
    @Autowired
    EventPublisher eventPublisher;
    @Autowired
    ILeaveRepository iLeaveRepository;
    @Autowired
    LeaveFactory leaveFactory;
 
    @Transactional
    public void createLeave(Leave leave, int leaderMaxLevel, Approver approver) {
        leave.setLeaderMaxLevel(leaderMaxLevel);
        leave.setApprover(approver);
        leave.create();
        iLeaveRepository.save(leaveFactory.createLeavePO(leave));
        LeaveEvent event = LeaveEvent.create(LeaveEventType.CREATE_EVENT, leave);
        iLeaveRepository.saveEvent(leaveFactory.createLeaveEventPO(event));
        eventPublisher.publish(event);
    }
 
    @Transactional
    public void updateLeaveInfo(Leave leave) {
        LeavePO po = leaveRepositoryInterface.findById(leave.getId());
        if (null == po) {
            throw new RuntimeException("leave不存在");
        }
        iLeaveRepository.save(leaveFactory.createLeavePO(leave));
    }
 
    @Transactional
    public void submitApproval(Leave leave, Approver approver) {
        LeaveEvent event;
        if ( ApprovalType.REJECT == leave.getCurrentApprovalInfo().getApprovalType()) {
            leave.reject(approver);
            event = LeaveEvent.create(LeaveEventType.REJECT_EVENT, leave);
        } else {
            if (approver != null) {
                leave.agree(approver);
                event = LeaveEvent.create(LeaveEventType.AGREE_EVENT, leave);
            } else {
                leave.finish();
                event = LeaveEvent.create(LeaveEventType.APPROVED_EVENT, leave);
            }
        }
        leave.addHistoryApprovalInfo(leave.getCurrentApprovalInfo());
        iLeaveRepository.save(leaveFactory.createLeavePO(leave));
        iLeaveRepository.saveEvent(leaveFactory.createLeaveEventPO(event));
        eventPublisher.publish(event);
    }
 
    public Leave getLeaveInfo(String leaveId) {
        LeavePO leavePO = iLeaveRepository.findById(leaveId);
        return leaveFactory.getLeave(leavePO);
    }
 
    public List<Leave> queryLeaveInfosByApplicant(String applicantId) {
        List<LeavePO> leavePOList = iLeaveRepository.queryByApplicantId(applicantId);
        return leavePOList.stream()
                .map(leavePO -> leaveFactory.getLeave(leavePO))
                .collect(Collectors.toList());
    }
 
    public List<Leave> queryLeaveInfosByApprover(String approverId) {
        List<LeavePO> leavePOList = iLeaveRepository.queryByApproverId(approverId);
        return leavePOList.stream()
                .map(leavePO -> leaveFactory.getLeave(leavePO))
                .collect(Collectors.toList());
    }
}

在应用服务组合不同聚合的领域服务时,通过Id或参数传参,尽量避免领域对象传参,以减少聚合之间的耦合度。

领域事件

领域事件基类

@Data
public class DomainEvent {
        String id;

        Date timestamp;

        String source;

        String data;
}
@Service
public class EventPublisher {
        public void publish(LeaveEvent event){
            //send to MQ
            //mq.send(event);
        }
}

领域事件实体

@Data
public class LeaveEvent extends DomainEvent {

        LeaveEventType leaveEventType;

        public static LeaveEvent create(LeaveEventType eventType, Leave leave){
              LeaveEvent event = new LeaveEvent();
              event.setId(IdGenerator.nextId());
              event.setLeaveEventType(eventType);
              event.setTimestamp(new Date());
              event.setData(JSON.toJSONString(leave));
              return event;
    	}
}

领域事件的执行逻辑:

  1. 执行业务逻辑,产生领域事件。
  2. 调用仓储接口,完成业务数据持久化。
  3. 调用仓储接口,完成事件数据持久化。
  4. 完成领域事件发布。

仓储模式

仓储接口

public interface ILeaveRepository {
        void save(LeavePO leavePO);

        void saveEvent(LeaveEventPO leaveEventPO);

        LeavePO findById(String id);

        List<LeavePO> queryByApplicantId(String applicantId);

        List<LeavePO> queryByApproverId(String approverId);
}

仓储实现

@Repository
public class LeaveRepositoryImpl implements ILeaveRepository {
      @Autowired
      LeaveDao leaveDao;

      @Autowired
      ApprovalInfoDao approvalInfoDao;

      @Autowired
      LeaveEventDao leaveEventDao;

      public void save(LeavePO leavePO) {
            leaveDao.save(leavePO);
            leavePO.getHistoryApprovalInfoPOList().forEach(approvalInfoPO -> approvalInfoPO.setLeaveId(leavePO.getId()));
            approvalInfoDao.saveAll(leavePO.getHistoryApprovalInfoPOList());
      }

      public void saveEvent(LeaveEventPO leaveEventPO){
            leaveEventDao.save(leaveEventPO);
      }

      @Override
      public LeavePO findById(String id) {
            return leaveDao.findById(id)
            .orElseThrow(() -> new RuntimeException("leave不存在"));
      }

      @Override
      public List<LeavePO> queryByApplicantId(String applicantId) {
              List<LeavePO> leavePOList = leaveDao.queryByApplicantId(applicantId);
              leavePOList.forEach(leavePO -> {
              List<ApprovalInfoPO> approvalInfoPOList = approvalInfoDao.queryByLeaveId(leavePO.getId());
              leavePO.setHistoryApprovalInfoPOList(approvalInfoPOList);
              });
              return leavePOList;
      }

      @Override
      public List<LeavePO> queryByApproverId(String approverId) {
              List<LeavePO> leavePOList = leaveDao.queryByApproverId(approverId);
              leavePOList.forEach(leavePO -> {
              List<ApprovalInfoPO> approvalInfoPOList = approvalInfoDao.queryByLeaveId(leavePO.getId());
              leavePO.setHistoryApprovalInfoPOList(approvalInfoPOList);
              });
              return leavePOList;
      }
}

这里为什么没有使用一对多、多对对呢?时间紧任务重或者并发量不高时可以使用,后期并发量起来了后,数据库将成为瓶颈。

JPA/Hibernate注解:@.NEToMany、@ManyToOne、@ManyToMany;

MyBatis注解:

@Results(id="", value={@Result(column="", property="", jdbcType=JdbcType.INTEGER),

@Result(column="", property="", JAVAType=xx.class,one=@One(select="com.xx..XxMapper.selectById"))

})

@Results(id="",value = { @Result(property = "", column = ""),

@Result(property = "xxList", javaType=List.class, many=@Many(select=""), column = "")});

Mybatis xml: association、collection。

 

工厂模式

工厂模式将与业务无关的职能从聚合根中剥离,放在工厂中统一创建和初始化。

//可考虑使用MapStruct
@Service
public class LeaveFactory {
      public LeavePO createLeavePO(Leave leave) {
            LeavePO leavePO = new LeavePO();
            leavePO.setId(UUID.randomUUID().toString());
            leavePO.setApplicantId(leave.getApplicant().getPersonId());
            leavePO.setApplicantName(leave.getApplicant().getPersonName());
            leavePO.setApproverId(leave.getApprover().getPersonId());
            leavePO.setApproverName(leave.getApprover().getPersonName());
            leavePO.setStartTime(leave.getStartTime());
            leavePO.setStatus(leave.getStatus());
            List<ApprovalInfoPO> historyApprovalInfoPOList = approvalInfoPOListFromDO(leave);
            leavePO.setHistoryApprovalInfoPOList(historyApprovalInfoPOList);
            return leavePO;
      }

      public Leave getLeave(LeavePO leavePO) {
          Leave leave = new Leave();
          Applicant applicant = Applicant.builder()
                  .personId(leavePO.getApplicantId())
                  .personName(leavePO.getApplicantName())
                  .build();
          leave.setApplicant(applicant);
          Approver approver = Approver.builder()
                .personId(leavePO.getApproverId())
                .personName(leavePO.getApproverName())
                .build();
          leave.setApprover(approver);
          leave.setStartTime(leavePO.getStartTime());
          leave.setStatus(leavePO.getStatus());
          List<ApprovalInfo> approvalInfos = getApprovalInfos(leavePO.getHistoryApprovalInfoPOList());
          leave.setHistoryApprovalInfos(approvalInfos);
          return leave;
      }

      public LeaveEventPO createLeaveEventPO(LeaveEvent leaveEvent){
            LeaveEventPO eventPO = new LeaveEventPO();
            eventPO.setLeaveEventType(leaveEvent.getLeaveEventType());
            eventPO.setSource(leaveEvent.getSource());
            eventPO.setTimestamp(leaveEvent.getTimestamp());
            eventPO.setData(JSON.toJSONString(leaveEvent.getData()));
            return eventPO;
      }

      private List<ApprovalInfoPO> approvalInfoPOListFromDO(Leave leave) {
            return leave.getHistoryApprovalInfos()
            .stream()
            .map(this::approvalInfoPOFromDO)
            .collect(Collectors.toList());
      }

      private ApprovalInfoPO approvalInfoPOFromDO(ApprovalInfo approvalInfo){
            ApprovalInfoPO po = new ApprovalInfoPO();
            po.setApproverId(approvalInfo.getApprover().getPersonId());
            po.setApproverLevel(approvalInfo.getApprover().getLevel());
            po.setApproverName(approvalInfo.getApprover().getPersonName());
            po.setApprovalInfoId(approvalInfo.getApprovalInfoId());
            po.setMsg(approvalInfo.getMsg());
            po.setTime(approvalInfo.getTime());
            return po;
      }

      private ApprovalInfo approvalInfoFromPO(ApprovalInfoPO approvalInfoPO){
            ApprovalInfo approvalInfo = new ApprovalInfo();
            approvalInfo.setApprovalInfoId(approvalInfoPO.getApprovalInfoId());
            Approver approver = Approver.builder()
            .personId(approvalInfoPO.getApproverId())
            .personName(approvalInfoPO.getApproverName())
            .level(approvalInfoPO.getApproverLevel())
            .build();
            approvalInfo.setApprover(approver);
            approvalInfo.setMsg(approvalInfoPO.getMsg());
            approvalInfo.setTime(approvalInfoPO.getTime());
            return approvalInfo;
      }

	private List<ApprovalInfo> getApprovalInfos(List<ApprovalInfoPO> approvalInfoPOList){
        return approvalInfoPOList.stream()
        .map(this::approvalInfoFromPO)
        .collect(Collectors.toList());
    }
}

服务的组合和编排

应用层的应用服务主要完成领域服务的组合与编排。

@Service
public class LeaveApplicationService {
    @Autowired
    LeaveDomainService leaveDomainService;
    @Autowired
    PersonDomainService personDomainService;
    @Autowired
    ApprovalRuleDomainService approvalRuleDomainService;
 
    /**
     * 创建一个请假申请并为审批人生成任务
     * @param leave
     */
    public void createLeaveInfo(Leave leave){
        int leaderMaxLevel = approvalRuleDomainService.getLeaderMaxLevel(leave.getApplicant().getPersonType(), leave.getType().toString(), leave.getDuration());
        Person approver = personDomainService.findFirstApprover(leave.getApplicant().getPersonId(), leaderMaxLevel);
        leaveDomainService.createLeave(leave, leaderMaxLevel, Approver.fromPerson(approver));
    }
 
    /**
     * 更新请假单基本信息
     * @param leave
     */
    public void updateLeaveInfo(Leave leave){
        leaveDomainService.updateLeaveInfo(leave);
    }
 
    /**
     * 提交审批,更新请假单信息
     * @param leave
     */
    public void submitApproval(Leave leave){
        //find next approver
        Person approver = personDomainService.findNextApprover(leave.getApprover().getPersonId(), leave.getLeaderMaxLevel());
        leaveDomainService.submitApproval(leave, Approver.fromPerson(approver));
    }
 
    public Leave getLeaveInfo(String leaveId){
        return leaveDomainService.getLeaveInfo(leaveId);
    }
 
    public List<Leave> queryLeaveInfosByApplicant(String applicantId){
        return leaveDomainService.queryLeaveInfosByApplicant(applicantId);
    }
 
    public List<Leave> queryLeaveInfosByApprover(String approverId){
        return leaveDomainService.queryLeaveInfosByApprover(approverId);
    }
}

服务接口的提供

facade门面接口主要抽象出Controller Api作为OpenFeign调用的接口门面类。

/**
* @author lyonardo
* @Description
* @createTime 2020年03月08日 15:06:00
*/
@FeignClient(name = "leave-service", path = "/leave")
public interface LeaveFeignClient {
      @PostMapping("/submit")
      Response submitApproval(LeaveDTO leaveDTO);

      @PostMapping("/{leaveId}")
      Response findById(@PathVariable String leaveId);
      /**
      * 根据申请人查询所有请假单
      * @param applicantId
      * @return
      */
      @PostMapping("/query/applicant/{applicantId}")
      Response queryByApplicant(@PathVariable String applicantId);
      /**
      * 根据审批人id查询待审批请假单(待办任务)
      * @param approverId
      * @return
      */
      @PostMapping("/query/approver/{approverId}")
      Response queryByApprover(@PathVariable String approverId)();
}

实现类

@RestController
@RequestMapping("/leave")
@Slf4j
public class LeaveApi {
    @Autowired
    LeaveApplicationService leaveApplicationService;

    @PostMapping("/create")
    public Response createLeaveInfo(LeaveDTO leaveDTO){
        Leave leave = LeaveAssembler.toDO(leaveDTO);
        leaveApplicationService.createLeaveInfo(leave);
        return Response.ok();
    }

    @PutMapping("/update")
    public Response updateLeaveInfo(LeaveDTO leaveDTO){
        Leave leave = LeaveAssembler.toDO(leaveDTO);
        leaveApplicationService.updateLeaveInfo(leave);
        return Response.ok();
    }

    @PostMapping("/submit")
    public Response submitApproval(LeaveDTO leaveDTO){
        Leave leave = LeaveAssembler.toDO(leaveDTO);
        leaveApplicationService.submitApproval(leave);
        return Response.ok();
    }

    @PostMapping("/{leaveId}")
    public Response findById(@PathVariable String leaveId){
      Leave leave = leaveApplicationService.getLeaveInfo(leaveId);
      return Response.ok(LeaveAssembler.toDTO(leave));
    }

    /**
    * 根据申请人查询所有请假单
    * @param applicantId
    * @return
    */
    @PostMapping("/query/applicant/{applicantId}")
    public Response queryByApplicant(@PathVariable String applicantId){
        List<Leave> leaveList = leaveApplicationService.queryLeaveInfosByApplicant(applicantId);
        List<LeaveDTO> leaveDTOList = leaveList.stream().map(leave -> LeaveAssembler.toDTO(leave)).collect(Collectors.toList());
        return Response.ok(leaveDTOList);
    }

    /**
    * 根据审批人id查询待审批请假单(待办任务)
    * @param approverId
    * @return
    */
    @PostMapping("/query/approver/{approverId}")
    public Response queryByApprover(@PathVariable String approverId){
            List<Leave> leaveList = leaveApplicationService.queryLeaveInfosByApprover(approverId);
            List<LeaveDTO> leaveDTOList = leaveList.stream().map(leave -> LeaveAssembler.toDTO(leave)).collect(Collectors.toList());
            return Response.ok(leaveDTOList);
      }
}

数据组装层

LeaveAssembler

//可使用MapStruct做对象转换和组装

public class LeaveAssembler {
	public static LeaveDTO toDTO(Leave leave){
        LeaveDTO dto = new LeaveDTO();
        dto.setLeaveId(leave.getId());
        dto.setLeaveType(leave.getType().toString());
        dto.setStatus(leave.getStatus().toString());
        dto.setStartTime(DateUtil.formatDateTime(leave.getStartTime()));
        dto.setEndTime(DateUtil.formatDateTime(leave.getEndTime()));
        dto.setCurrentApprovalInfoDTO(ApprovalInfoAssembler.toDTO(leave.getCurrentApprovalInfo()));
        List<ApprovalInfoDTO> historyApprovalInfoDTOList = leave.getHistoryApprovalInfos()
        .stream()
        .map(historyApprovalInfo -> ApprovalInfoAssembler.toDTO(leave.getCurrentApprovalInfo()))
        .collect(Collectors.toList());
        dto.setHistoryApprovalInfoDTOList(historyApprovalInfoDTOList);
        dto.setDuration(leave.getDuration());
        return dto;
	}

	public static Leave toDO(LeaveDTO dto){
        Leave leave = new Leave();
        leave.setId(dto.getLeaveId());
        leave.setApplicant(ApplicantAssembler.toDO(dto.getApplicantDTO()));
        leave.setApprover(ApproverAssembler.toDO(dto.getApproverDTO()));
        leave.setCurrentApprovalInfo(ApprovalInfoAssembler.toDO(dto.getCurrentApprovalInfoDTO()));
        List<ApprovalInfo> historyApprovalInfoDTOList = dto.getHistoryApprovalInfoDTOList()
        .stream()
        .map(ApprovalInfoAssembler::toDO)
        .collect(Collectors.toList());
        leave.setHistoryApprovalInfos(historyApprovalInfoDTOList);
        return leave;
	}
}


Tags:   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
新增融券再启动暂停键,有头部券商融券池全部收回!融券余额已较年初下降近四成
4月11日,A股市场触底反弹。其中,有一则消息是触发市场反弹的重要原因:据称,多家券商暂停新增融券通券源,拟阶段性临停融券通券源每日新增投放。《每日经济新闻》向某华东头部券商...【详细内容】
2024-04-11  Search:   点击:(2)  评论:(0)  加入收藏
16个Redis常见使用场景总结
来源:blog.csdn.net/qq_39938758/article/details/105577370目录 缓存 数据共享分布式 分布式锁 全局ID 计数器 限流 位统计 购物车 用户消息时间线timeline 消息...【详细内容】
2024-04-11  Search:   点击:(2)  评论:(0)  加入收藏
一篇文章教会你使用Python中三种简单的函数
所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。一、函数简介所谓函数,就是指:把某些特定功能的代码组成为一个整体,这个整体就叫做函数。二、函数定义...【详细内容】
2024-04-11  Search:   点击:(2)  评论:(0)  加入收藏
聊聊Rust里面的数据类型
嘿,朋友们!今天我们来聊聊Rust里面的数据类型。你知道吗?Rust的数据类型可是很重要的哦,它们帮助我们定义变量和函数可以处理什么样的数据。基本数据类型首先,让我们来看看Rust提...【详细内容】
2024-04-11  Search:   点击:(2)  评论:(0)  加入收藏
C++中的外部模板及其在当前编译文件中的实例化
在C++中,模板是一种泛型编程的工具,它允许程序员以一种类型无关的方式编写代码。然而,模板的一个常见问题是它们会导致编译时间增加,特别是在大型项目中,当多个源文件包含相同的...【详细内容】
2024-04-11  Search:   点击:(2)  评论:(0)  加入收藏
一篇文章带你了解Python的分布式进程接口
在Thread和Process中,应当优选Process,因为Process更稳定,而且,Process可以分布到多台机器上,而Thread最多只能分布到同一台机器的多个CPU上。一、前言在Thread和Process中,应当优...【详细内容】
2024-04-11  Search:   点击:(2)  评论:(0)  加入收藏
网络安全行业的春天何时来?
2023年下半年开始,网络安全从业人员都感受到了网安行业的寒冬,但是其实前奏并不是此刻,只是涉及到大量裁员关乎自身而人人感同身受。从近五年各个网络安全上市公司财报可以发现...【详细内容】
2024-04-11  Search:   点击:(2)  评论:(0)  加入收藏
Linux获取Redis 性能指标方法
一、监控指标&Oslash; 性能指标:Performance&Oslash; 内存指标: Memory&Oslash; 基本活动指标:Basic activity&Oslash; 持久性指标: Persistence&Oslash; 错误指标:Error二、监...【详细内容】
2024-04-11  Search:   点击:(3)  评论:(0)  加入收藏
Redis与缓存一致性问题
缓存一致性问题是在使用缓存系统,如Redis时经常遇到的问题。当数据在原始数据源(如数据库)中发生变化时,如何确保缓存中的数据与数据源保持一致,是开发者需要关注的关键问题。一...【详细内容】
2024-04-11  Search:   点击:(2)  评论:(0)  加入收藏
10余所高校公布强基计划,今年有哪些变化?
今天,中国人民大学、中国农业大学、复旦大学、武汉大学、山东大学、吉林大学、重庆大学、大连理工大学发布了2024年强基计划招生简章。目前,已有10余所高校发布了招生简章。它...【详细内容】
2024-04-11  Search:   点击:(2)  评论:(0)  加入收藏
▌简易百科推荐
Web Components实践:如何搭建一个框架无关的AI组件库
一、让人又爱又恨的Web ComponentsWeb Components是一种用于构建可重用的Web元素的技术。它允许开发者创建自定义的HTML元素,这些元素可以在不同的Web应用程序中重复使用,并且...【详细内容】
2024-04-03  京东云开发者    Tags:Web Components   点击:(8)  评论:(0)  加入收藏
Kubernetes 集群 CPU 使用率只有 13% :这下大家该知道如何省钱了
作者 | THE STACK译者 | 刘雅梦策划 | Tina根据 CAST AI 对 4000 个 Kubernetes 集群的分析,Kubernetes 集群通常只使用 13% 的 CPU 和平均 20% 的内存,这表明存在严重的过度...【详细内容】
2024-03-08  InfoQ    Tags:Kubernetes   点击:(12)  评论:(0)  加入收藏
Spring Security:保障应用安全的利器
SpringSecurity作为一个功能强大的安全框架,为Java应用程序提供了全面的安全保障,包括认证、授权、防护和集成等方面。本文将介绍SpringSecurity在这些方面的特性和优势,以及它...【详细内容】
2024-02-27  风舞凋零叶    Tags:Spring Security   点击:(54)  评论:(0)  加入收藏
五大跨平台桌面应用开发框架:Electron、Tauri、Flutter等
一、什么是跨平台桌面应用开发框架跨平台桌面应用开发框架是一种工具或框架,它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上...【详细内容】
2024-02-26  贝格前端工场    Tags:框架   点击:(47)  评论:(0)  加入收藏
Spring Security权限控制框架使用指南
在常用的后台管理系统中,通常都会有访问权限控制的需求,用于限制不同人员对于接口的访问能力,如果用户不具备指定的权限,则不能访问某些接口。本文将用 waynboot-mall 项目举例...【详细内容】
2024-02-19  程序员wayn  微信公众号  Tags:Spring   点击:(39)  评论:(0)  加入收藏
开发者的Kubernetes懒人指南
你可以将本文作为开发者快速了解 Kubernetes 的指南。从基础知识到更高级的主题,如 Helm Chart,以及所有这些如何影响你作为开发者。译自Kubernetes for Lazy Developers。作...【详细内容】
2024-02-01  云云众生s  微信公众号  Tags:Kubernetes   点击:(50)  评论:(0)  加入收藏
链世界:一种简单而有效的人类行为Agent模型强化学习框架
强化学习是一种机器学习的方法,它通过让智能体(Agent)与环境交互,从而学习如何选择最优的行动来最大化累积的奖励。强化学习在许多领域都有广泛的应用,例如游戏、机器人、自动驾...【详细内容】
2024-01-30  大噬元兽  微信公众号  Tags:框架   点击:(68)  评论:(0)  加入收藏
Spring实现Kafka重试Topic,真的太香了
概述Kafka的强大功能之一是每个分区都有一个Consumer的偏移值。该偏移值是消费者将读取的下一条消息的值。可以自动或手动增加该值。如果我们由于错误而无法处理消息并想重...【详细内容】
2024-01-26  HELLO程序员  微信公众号  Tags:Spring   点击:(86)  评论:(0)  加入收藏
SpringBoot如何实现缓存预热?
缓存预热是指在 Spring Boot 项目启动时,预先将数据加载到缓存系统(如 Redis)中的一种机制。那么问题来了,在 Spring Boot 项目启动之后,在什么时候?在哪里可以将数据加载到缓存系...【详细内容】
2024-01-19   Java中文社群  微信公众号  Tags:SpringBoot   点击:(86)  评论:(0)  加入收藏
花 15 分钟把 Express.js 搞明白,全栈没有那么难
Express 是老牌的 Node.js 框架,以简单和轻量著称,几行代码就可以启动一个 HTTP 服务器。市面上主流的 Node.js 框架,如 Egg.js、Nest.js 等都与 Express 息息相关。Express 框...【详细内容】
2024-01-16  程序员成功  微信公众号  Tags:Express.js   点击:(88)  评论:(0)  加入收藏
相关文章
    无相关信息
站内最新
站内热门
站内头条