CAPEX、DCF、NPV、IRR、ROI

December 21st, 2021 by JasonLe's Tech 1,200 views

CAPEX 是指资本性支出,英文全称为capital expenditure。一般指资金或固定资产、无形资产、递延资产等资产的投入。此类资产在使用过程中会持续多个计费期间,需要在使用过程中将其资本化,并分期将成本转化为费用。如固定资产的折旧,无形资产和递延资产的摊销等。

贴现现金流(Discounted Cash Flow,DCF)贴现现金流是用来评估一个投资机会的吸引力的方法。指将未来某年的现金收支折算为目前的价值。未来现金流的现值必须通过重新计算(折算)来确定。如此,一个公司或计划项目才能够被准确估值。DCF包括NPV和IRR

净现值(NPV)是一项投资所产生的未来现金流的折现值与项目投资成本之间的差值,净现值指标是反映项目投资获利能力的指标

决策标准:

  • 净现值≥0 方案可行;
  • 净现值<0 方案不可行;
  • 净现值均>0 净现值最大的方案为最优方案。

优点:

  • 考虑了资金时间价值,增强了投资经济性的评价;
  • 考虑了全过程的净现金流量,体现了流动性与收益性的统一;
  • 考虑了投资风险大则采用高折现率,风险小则采用低折现率。

缺点:

  • 净现值的计算较麻烦,难掌握;
  • 净现金流量的测量和折现率较难确定;
  • 不能从动态角度直接反映投资项目的实际收益水平;
  • 项目投资额不等时,无法准确判断方案的优劣。

IRR(Internal Rate of Return)是项目NPV为0的折现率,表示考虑资金的时间价值时,未来现金流流入与流出的现值相等时的折现率,这与NPV的计算逻辑相通,只是在NPV计算中,折现率是已知条件,而IRR计算中是计算目标。

投资回报率(ROI,Return On Investment),是指达产期正常年度利润或年均利润占投资总额的百分比。其计算公式为:投资回报率(ROI)=年利润或年均利润/投资总额×100%

投资回报率的优点是计算简单;缺点是没有考虑资金时间价值因素,不能正确反映建设期长短及投资方式不同和回收额的有无等条件对项目的影响,分子、分母计算口径的可比性较差,无法直接利用无风险投资利润率的投资项目才具有财务可行性,ROI往往具有时效性–回报通常是基于某些特定年份。

参考:

http://jinrongbaike.com/doc-view-7554.htm

JDK8版本过高引起MySQL连接失败:javax.net.ssl.SSLHandshakeException: No appropriate protocol

September 28th, 2021 by JasonLe's Tech 8,217 views

最近在做面向k8s的项目镜像,遇到很奇怪的问题,SpringCloud 项目连接mysql 抛 javax.net.ssl.SSLHandshakeException: No appropriate protocol,调查一段时间后,发现是Java security中的配置不对导致连接Mysql异常。

javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate)
	at sun.security.ssl.HandshakeContext.<init>(HandshakeContext.java:171) ~[na:1.8.0_292]
	at sun.security.ssl.ClientHandshakeContext.<init>(ClientHandshakeContext.java:98) ~[na:1.8.0_292]
	at sun.security.ssl.TransportContext.kickstart(TransportContext.java:220) ~[na:1.8.0_292]
	at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:428) ~[na:1.8.0_292]
	at com.mysql.cj.protocol.ExportControlled.performTlsHandshake(ExportControlled.java:316) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	at com.mysql.cj.protocol.StandardSocketFactory.performTlsHandshake(StandardSocketFactory.java:188) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	at com.mysql.cj.protocol.a.NativeSocketConnection.performTlsHandshake(NativeSocketConnection.java:99) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:331) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	... 68 common frames omitted

看到SSLHandshakeException,心里打起了问号?这个错误比较反常,最终网上找了一番,问题定位了:JDK8高版本导致的,因为之前用的是oracle:1.8,然后换成了openjdk:8,然后发现项目无法启动了。

方法一:

此处连接的MySQL,导致的报错,修改jdbcUrl,在其后面加useSSL=false后运行正常

方法二:

删除SSLv3, TLSv1, TLSv1.1并保存java.security文件,重启项目即可解决问题,删除后此处为:

# Example:
#   jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048
jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \
    DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \
    include jdk.disabled.namedCurves

方法三:

降低JDK版本,这个相当也容易操作,比如可以从1.8.0_292降到1.8.0_281,甚至是1.8.0_275版本,但个人不建议,因为Oracle对JDK 8的支持一直会到2030年:即使很长一段时间用JDK 8,但JDK 8本身也是有小版本迭代的,比如你明年换了电脑,安装JDK,基本是1.8.0_292之后的版本,那这个问题会一直存在。

方法四:

方法一 能解决由SSL调用权限导致的所有问题,但破坏了安全性
方法二 针对MySQL的问题,可以快速解决
方法三 不推荐

因此,碰到类似问题,基本的思路是兼容JDK 8高版本甚至JDK高版本,比如代码层面的:

private Socket overrideTlsProtocol(final Socket socket) {
    if (!(socket instanceof SSLSocket)) {
        throw new RuntimeException("Error, an instance of SSLSocket is expected");
    }
    ((SSLSocket) socket).setEnabledProtocols(new String[]{"SSLv3"});
    return socket;
}

修改为:

private Socket overrideTlsProtocol(final Socket socket) {
    if (!(socket instanceof SSLSocket)) {
        throw new RuntimeException("Error, an instance of SSLSocket is expected");
    }
    ((SSLSocket) socket).setEnabledProtocols(new String[]{"SSLv3", "TLSv1","TLSv1.1"});
    return socket;
}

K8s核心概念

August 29th, 2021 by JasonLe's Tech 901 views

Pod

  • Pod是K8s中最小的单元
  • 一组容器的集合
  • 共享网络【一个Pod中的所有容器共享同一网络】
  • 生命周期是短暂的(服务器重启后,就找不到了)

Volume

  • 声明在Pod容器中可访问的文件目录
  • 可以被挂载到Pod中一个或多个容器指定路径下
  • 支持多种后端存储抽象【本地存储、分布式存储、云存储】

Controller

  • 确保预期的pod副本数量【ReplicaSet】
  • 无状态应用部署【Deployment】
    • 无状态就是指,不需要依赖于网络或者ip
  • 有状态应用部署【StatefulSet】
    • 有状态需要特定的条件
  • 确保所有的node运行同一个pod 【DaemonSet】
  • 一次性任务和定时任务【Job和CronJob】

Deployment

  • 定义一组Pod副本数目,版本等
  • 通过控制器【Controller】维持Pod数目【自动回复失败的Pod】
  • 通过控制器以指定的策略控制版本【滚动升级、回滚等】

Service

  • 定义一组pod的访问规则
  • Pod的负载均衡,提供一个或多个Pod的稳定访问地址
  • 支持多种方式【ClusterIP、NodePort、LoadBalancer】

Label

label:标签,用于对象资源查询,筛选

Namespace

命名空间,逻辑隔离

  • 一个集群内部的逻辑隔离机制【鉴权、资源】
  • 每个资源都属于一个namespace
  • 同一个namespace所有资源不能重复
  • 不同namespace可以资源名重复

API

我们通过Kubernetes的API来操作整个集群

同时我们可以通过 kubectl 、ui、curl 最终发送 http + json/yaml 方式的请求给API Server,然后控制整个K8S集群,K8S中所有的资源对象都可以采用 yaml 或 json 格式的文件定义或描述

如下:使用yaml部署一个nginx的pod

完整流程

  • 通过Kubectl提交一个创建RC(Replication Controller)的请求,该请求通过APlserver写入etcd
  • 此时Controller Manager通过API Server的监听资源变化的接口监听到此RC事件
  • 分析之后,发现当前集群中还没有它所对应的Pod实例
  • 于是根据RC里的Pod模板定义一个生成Pod对象,通过APIServer写入etcd
  • 此事件被Scheduler发现,它立即执行执行一个复杂的调度流程,为这个新的Pod选定一个落户的Node,然后通过API Server讲这一结果写入etcd中
  • 目标Node上运行的Kubelet进程通过APiserver监测到这个”新生的Pod.并按照它的定义,启动该Pod并任劳任怨地负责它的下半生,直到Pod的生命结束
  • 随后,我们通过Kubectl提交一个新的映射到该Pod的Service的创建请求
  • ControllerManager通过Label标签查询到关联的Pod实例,然后生成Service的Endpoints信息,并通过APIServer写入到etod中,
  • 接下来,所有Node上运行的Proxy进程通过APIServer查询并监听Service对象与其对应的Endponts信息,建立一个软件方式的负载均衡器来实现Service访问到后端Pod的流量转发功能

 

 

分布式架构关键设计

May 24th, 2021 by JasonLe's Tech 705 views

三种数据库方案

1. 一体化分布式数据库方案
它支持数据多副本、高可用。多采用 Paxos 协议,一次写入多数据副本,多数副本写入成即算成功。代表产品是 OceanBase 和高斯数据库。

2. 集中式数据库 + 数据库中间件方案
它是集中式数据库与数据库中间件结合的方案,通过数据库中间件实现数据路由和全局数据管理。数据库中间件和数据库独立部署,采用数据库自身的同步机制实现主副本数据的一致性。集中式数据库主要有 MySQL 和 PostgreSQL 数据库,基于这两种数据库衍生出了很多的解决方案,比如开源数据库中间件 MyCat+MySQL 方案,TBase(基于PostgreSQL,但做了比较大的封装和改动)等方案。

3. 集中式数据库 + 分库类库方案
它是一种轻量级的数据库中间件方案,分库类库实际上是一个基础 JAR 包,与应用软件部署在一起,实现数据路由和数据归集。它适合比较简单的读写交易场景,在强一致性和聚合分析查询方面相对较弱。典型分库基础组件有 ShardingSphere。

跨库关联查询如何处理?

由于数据分散在不同微服务里,我们无法跨多个微服务来统计这些数据。可以建立面向主题的分布式数据库,它的数据来源于不同业务的微服务。采用数据库日志捕获技术,从各业务端微服务将数据准实时汇集到主题数据库。在数据汇集时,提前做好数据关联(如将多表数据合并为一个宽表)或者建立数据模型。面向主题数据库建设查询微服务。这样一次查询就可以获取客户所有维度的业务数据了。还可以根据主题或场景设计合适的分库主键,提高查询效率。

对于不在同一个数据库的表与表之间的关联查询场景,可以采用小表广播,在业务库中增加一张冗余的代码副表。当主表数据发生变化时,可以通过消息发布和订阅的领域事件驱动模式,异步刷新所有副表数据。这样既可以解决表与表的关联查询,还可以提高数据的查询效率。

前后序业务数据的处理

前后序的数据都跟领域事件有关。可以通过领域事件处理机制,按需将前序数据通过领域事件实体,传输并冗余到当前的微服务数据库中。可以将前序数据设计为实体或者值对象,并被当前实体引用。

在设计时需要关注以下内容:如果前序数据在当前微服务只可整体修改,并且不会对它做查询和统计分析,可以将它设计为值对象;当前序数据是多条,并且需要做查询和统计分析,可以将它设计为实体。

可以在货物运输微服务,一次获取前序订单的清单数据和货物运输单数据(类似BFF),将所有数据一次反馈给前端应用,降低跨微服务的调用。如果前序数据被设计为实体,还可以将前序数据作为查询条件,在本地微服务完成多维度的综合数据查询。只有必要时才从前序微服务,获取前序实体的明细数据。这样,既可以保证数据的完整性,还可以降低微服务的依赖,减少跨微服务调用,提升系统性能。

DDD与微服务的服务流转关系

April 29th, 2021 by JasonLe's Tech 735 views

微服务调用关系

DDD服务调用关系:

数据持久化对象 PO(Persistent Object),与数据库结构一一映射,是数据持久化过程中的数据载体。

领域对象 DO(Domain Object),微服务运行时的实体,是核心业务的载体。

数据传输对象 DTO(Data Transfer Object),用于前端与应用层或者微服务之间的数据组装和传输,是应用之间数据传输的载体。

视图对象 VO(View Object),用于封装展示层指定页面或组件的数据。

领域层

领域层实现核心业务逻辑,负责表达领域模型业务概念、业务状态和业务规则。主要的服务形态有实体方法和领域服务。

实体采用充血模型,在实体类内部实现实体相关的所有业务逻辑,实现的形式是实体类中的方法。实体是微服务的原子业务逻辑单元。在设计时我们主要考虑实体自身的属性和业务行为,实现领域模型的核心基础能力。不必过多考虑外部操作和业务流程,这样才能保证领域模型的稳定性。

DDD 提倡富领域模型,尽量将业务逻辑归属到实体对象上,实在无法归属的部分则设计成领域服务。领域服务会对多个实体或实体方法进行组装和编排,实现跨多个实体的复杂核心业务逻辑。

对于严格分层架构,如果单个实体的方法需要对应用层暴露,则需要通过领域服务封装后才能暴露给应用服务。