Paul Jiang's Blog

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

C++ API设计-设计

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

设计API的基本工作流程:从问题分析开始,设计解决方案,然后实现设计方案。

技术债

技术债可以分为两类:
无意识的:软件是完全用心设计,结果却出现了问题,可能因为代码中引入了初级工程师的低质量代码,或者是因为兼并了其他公司,而被兼并的公司的软件存在各种问题等。
有意识的:由于时间、成本或资源限制等各方面压力而有意识地走捷径,寄希望于满足最后期限,之后再实施“正确的”方案。

当软件库正在接近最后期限,会出现如下4个警告信号:

  • 脆弱性:系统中一个地方的改动会引发
  • 僵化性:软件难以改动。
  • 顽固性:顽固的代码难以复用,有的实现可能与周边代码缠绕在一起。
  • 不可转移性:如果团队中只有一个工程师可以处理代码中的某个部分,我们就说出现了不可转移性。

因为积累了太多技术债,要花费很多时间维护和包含遗留代码,这比为客户增加新特性花费的时间还要多。这往往导致下一代项目需要修复老系统的问题。
从设计思路上讲,对于这样的下一代项目,其开发有以下两个方向。

  1. 演进式。设计一个包含所有新需求的系统,然后迭代重构现有系统直到满足要求。
  2. 革命式。丢掉旧代码,然后从零开始设计,实现一个全新的系统。

同样你要留意第二系统综合症,即新系统可能因为野心太大或工程实现过于复杂而失败。

API设计的元素

产生好的API设计的秘诀是:对问题领域进行合理抽象,然后设计相应的对象与类的层次结构来表达该抽象。
Grady Booch认为任何复杂系统都有两种层次化视角

  1. 对象层次结构
  2. 类层次结构

一般认为软件构建的设计阶段由两个主要活动组成。

  1. 架构设计。描述软件的顶层结构与组织。
  2. 详细设计。在一个足够详细的层面描述设计中单独的组件,组件可以利用此描述信息来实现。

C++ API设计-介绍

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 架构

API

API提供了对某个问题的抽象,以及客户与解决该问题的软件组件之间进行交互的方式。

API是一个明确定义的接口,可以为其他软件提供特定服务。
现代应用程序通常都是基于很多API建立起来的,而这些API往往又依赖于其他API。
目的是为某个组件的功能提供一个逻辑接口,同时隐藏该模块内部的实现细节。

API设计中的一些关键因素

  1. API是为开发者设计的接口,如同GUI是为用户设计的接口一样。
  2. 多个应用程序可以共享一个API。
  3. 修改API时,必须竟可能保证向后兼容。

API的生成周期一般比较长。为了创造良好的API,在前期要投入巨大的成本,因为规划、设计、版本和审查开销是必不可少的。

API描述了其他工程师构建他们的应用软件所使用的软件。因此,API必须拥有良好的设计、文档、回归测试,并且保证发布之间的稳定性。

为什么使用API

  1. 更健壮的代码
  • 隐藏实现
  • 延长寿命
  • 促进模块化
  • 减少代码重复
  • 消除硬编码假设
  • 易于改变实现
  • 易于优化
  1. 代码复用
    代码复用就是使用已有的软件去构建新的软件。软件开发从本质上已变得更加模块化。
  2. 并行开发
    假设你正在编写一个字符串加密算法,其他开发者需要使用该算法将数据写入配置文件。一种做法是让其他开发者等你完成所有工作,然后在其文件输出模块中使用你的算法。然而,更有效率的做法是,你们提前见面协商好恰当的API,然后你把API放在适当的位置,而API仅仅其占位符的作用。
    实际上,有些接口问题很可能在编写代码之前并没有预料到,因此API设计可能需要多次迭代才能保证其恰到好处。

何时应当避免使用API

如果编写的是不需要和其他客户端通信的内部模块,那么为模块创建并维护稳定公有接口的额外开销就不值得了。
假设你是一位想在应用程序中使用第三方API的软件开发人员,在如下情况下避免使用特定的API:

  • 许可证限制
  • 功能不匹配
  • 缺少源代码
  • 缺乏文档

FastDFS-安装

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 中间件

Kafka-安装

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 中间件

Redis-安装

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 中间件

Tomcat-性能调校

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 中间件

调校的基本步骤

  1. 决定需要测量的对象。
  2. 决定测量的方式。
  3. 测量。
  4. 从测量结果中,了解其含义。
  5. 以预期会提升测量结果的方式来修补系统配置。
  6. 测量,并与之前的测量结果比较。
  7. 回到第4步。

依照实际环境架设Tomcat。试着使用相同的硬件、操作系统、数据库等。越接近实际环境就越可能找到在实际系统中的瓶颈。在另一台机器上安装并配置负载测试用的软件。如果在运行Tomcat的同一台机器上运行,测试结果会不可靠,有时会更糟糕。
让负责测试的机器与运行Tomcat的机器之间通信隔离。这样处理的原因在于在运行高流量的测试时,如果追加了不属于测试的网络流量,将导致这些测试结果变得不可靠。

测量Web服务器的性能

  1. 负载测试客户端与服务器软件包是在同一台机器吗?
  2. 在测试时,服务器端机器是否正在执行其他的程序?
  3. 客户端与服务器是以千兆位的以太网络,还是100baseT、10baseT相连呢?如果负载测试客户端机器通过低于千兆以太网速度的网络信道链接到服务器端,那么网络链接会降低测试速度,从而改变测试结果。
  4. 是屡次请求相同的网页,混合多种不同的并发请求,还是从庞大的网页清单中随机挑选呢?这能影响服务器及多线程缓存性能。
  5. 测试客户端应按常规方式还是应该以突发方式发送请求呢?按基准调校来说,当您欲知道多快的服务器能完成请求时,应使测试客户端在请求之间不经暂停、快速连续地发送请求。
  6. 运行的服务器是最终配置吗?根据基准调校的需要,应关闭所有调试工作,且还可能要关闭一些日志。
  7. HTTP客户端请求了图像还是仅请求了嵌入于其中的HTML页?

测试负载的工具

  1. Jmeter
    Jmeter提供了定制测试结果的最具报表特性的选项,是最能适应不同操作系统的工具,而且支持大部分功能。但是,出于某种原因,Jmeter不能像ab和siege所做的那样,每秒钟请求并完成那么多次HTTP请求。
  2. ab
    是一个基准调校工具,所以一般不会把它用于递归测试。
  3. Siege
    Siege是另一个很好的命令行Web负载测试工具。Siege支持多种不同的认证特性,并可执行基准测试、递归测试,还支持”Internet”模式测试(即尽可能仿真在Internet上Web应用程序进行交互的众多实际用户所产生的负载)。

ab:Apache基准调校工具

在基准调校测试的时候,测试客户端请求HTTP的数量越少,测试客户端提供的测试结果就可能越不精确,因为在基准调校过程中,Java VM的资源回收暂停占用了整个测试时间的较高比重。运行HTTP请求的总数量越多,资源回收暂停变得意义更小,而且基准调校测试结果将更能显示Tomcat的整体性能。您最好运行至少100000个HTTP请求以执行基准调校测试。另外,您可以配置测试客户端,产生您喜欢的客户端线程数,但是,如果您将该线程数设置成超过Tomcat的conf/server.xml文件中提供的连接器(Connector)最大线程数(maxThread),那么实际不会有太大的帮助。在默认的情况下将该线程数设置为150。如果您设置成测试者超过这一数字,而且在更多的线程中产生了比Tomcat拥有的接收线程数和处理线程数还要多的请求,就会影响性能,因为一些客户端请求线程将一直处于等待状态。最好是就保留在连接器的最下线程数(Connector的maxThread)设置下,如使用149的客户端线程。

1
ab -k -n 100000 -c 149 http://127.0.0.1:8080/test

如果在ab命令行中删除-k,那么ab与Tomcat将不使用保持联机(keep-alive)链接,这样处理效率更低,因为它必须链接一个新的TCP包到Tomcat上,以执行每个HTTP请求。结果是每秒要处理的请求数更好了,而且Tomcat到客户端的吞吐量更小了。

1
ab -n 100000 -c 149 http://127.0.0.1:8080/test

外部调校

当您了解应用程序及Tomcat的实例对负载的响应情形后,就可以开始一些性能调校的处理了,这里有两种基本的调校方式:

  • 外部调校
    涉及到非Tomcat组件的调校,如Tomcat所运行的操作系统,以及运行Tomcat的Java虚拟机器。
    1.JVM的性能
    不同厂商,不同版本之间的基准调校。如果选择Java 1.6,那么您可以对Sun的1.6、IBM的1.6和BEA的1.6版本的虚拟机进行基准调校。
    通常,人们会设法修改JVM启动参数,以使Tomcat JVM能更快地为Web应用程序网页服务。这可以提供一些帮助,但通常不会在性能上提高较高百分比。其主要原因是这样处理不会太有用:JVM提供商在发布JDK之前就已进行过自身测试,寻找哪些设置会产生最佳性能,并让这些设置变成默认值。
    JVM的堆栈内存(heap memory)分配除外。JVM提供商默认选择分配少量内存以启动JVM,且如果Java应用程序需要更多内存,那么在应用程序暂停时,JVM的heap会被重新分配为更大。为了避免这种暂停,可以设置最小heap大小与最大heap大小相等。那样处理的话,JVM将不会在运行的时候尝试扩展heap大小。只要设置JAVA_OPTS环境变量为类似于-Xms512M -Xmx512M之类的值。这意味着最大、最小heap都被设置为512MB。
    2.操作系统的性能
    您的服务器的操作系统是最适合于运行大型、高容量的Web服务器吗?OpenBSD的主要目标在安全防护,所以其核心中许多的限制都设得比较低,以防止各种形式的DOS攻击。Linux的目标是容易使用,所以其限制值会设得比较高。如果您执行任何版本的Microsoft Windows,请确定是Server。在非Server的版本中,终端用户的授权协议和程序代码都可能会限制用户数、可使用的网络连接数或其他限制。
  • 内部调校
    与Tomcat本身有关的调校,包括改变配置文件中的设定值,以及修改Tomcat本身的源代码。
  1. 停用DNS查询
    DNS查询需要耗费网络资源,并涉及多部服务器的往返响应,而这些服务器可能位于遥远的地方,也可能尚未运行,因此导致延迟。
1
2
3
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
enableLookups="true" redirectPort="8443" />

只要将enableLookups的值”true”改成”false”,然后重新启动Tomcat。这样将不再使用DNS查询,也不会有延迟了。

  1. 调整线程数
    另一种对应用程序之Connector性能控制的方法是控制其处理的进程数。Tomcat默认使用线程池以便对传入的请求提供快速响应。
    通过更改Connector的minThreads与maxThreads的值,可以控制所分配的线程数。
    minThreads的值应该大到能够处理最小负载量。即如果在低谷时刻,每秒可访问五次网页,而每次请求在一秒钟之内可完成,则预先分配5个线程就够了。随后在高峰时刻,则需要分配更多线程(最多到maxThreads所设定的数目),为了防止流量暴增(或黑客的DoS攻击)让系统超出JVM的最大内存限制,而使服务器瘫痪,因此一定要设定上限。
  2. 加快JSP的编译速度
    当第一次访问JSP时,此JSP会转换成Java Servlet的源代码,然后必须再编译成Java的bytecode。用请求预编译JSP。

容量规划

调校Tomcat在线服务器性能的另一重要部分就是容量规划。无论调校多少配置文件与进行多少测试,如果没有符合网站预期流量大小的硬件与带宽,实际上也帮不了忙。
通过研究及(或)预测网站必须处理的总网络流量,决定可接收的服务质量与寻找满足或超越服务器软件需求,来预测网站所需的计算机硬件、操作系统与带宽的活动。

  1. 经验式的容量规划
    经验式的容量规划师一种轻量级的规划方式,它不一定要很精确,但已足够让公司免于完全不做容量规划的后果。这种方法符合以前的业界经验产生的容量与性能趋势。例如,对于网站在高峰时有多少输出流量,您可以做最佳的预测,然后将该数字加倍。
  2. 企业容量规划的目的是要更精确,因此也需要更久的时间。通常这会使用商业及的容量规划分析软件,以及反复的测试与模拟仿真。

Tomcat上的容量规划

  1. 服务器计算机硬件
  2. 网络带宽
  3. 服务器操作系统

适用于Tomcat的各种容量规划的一般程序

  1. 找出负载特性。
  2. 分析性能的趋势。
  3. 决定可接收服务的最低需求。例如,可能希望让用户最多等待20秒钟。
  4. 决定要使用何种基础体系的资源,并在分段的环境下测试。
  5. 如果第4步可以符合您的服务需求,就可以订购并部署更多的相同的机器,以作为生产用的服务器计算机。否则,请重复第4步,直到满足服务需求为止。

Zookeeper-安装

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 中间件

环境

  • CentOS Linux release 7.4.1708
  • OpenJDK Runtime Environment (build 1.8.0_131-b12)
  • zookeeper-3.4.8

配置

伪集群

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# begin 1

# zoo.cfg
dataDir=/root/zookeeper-3.4.8-1/data
clientPort=2081

server.0=10.0.2.15:2788:3788
server.1=10.0.2.15:2888:3888
server.2=10.0.2.15:2988:3988
# /root/zookeeper-3.4.8-1/data/myid
0
# end 1

# begin 2
# zoo.cfg
dataDir=/root/zookeeper-3.4.8-2/data
clientPort=2181

server.0=10.0.2.15:2788:3788
server.1=10.0.2.15:2888:3888
server.2=10.0.2.15:2988:3988
# /root/zookeeper-3.4.8-2/data/myid
1
# end 2

# begin 3
# zoo.cfg
dataDir=/root/zookeeper-3.4.8-3/data
clientPort=2281

server.0=10.0.2.15:2788:3788
server.1=10.0.2.15:2888:3888
server.2=10.0.2.15:2988:3988
# /root/zookeeper-3.4.8-3/data/myid
2
# end 3

启动

1
2
3
4
zkServer.sh start
zkServer.sh status

zkCli.sh

产品经理手册

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 产品

计算机

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 A概览

基础

计算机组成

计算机组成

汇编

单片机

C

操作系统

虚拟机

Java

数据结构

算法

编译器-介绍

发表于 2018-11-16 | 更新于 2020-05-17 | 分类于 语言

语言处理器

编译器:阅读以某一种语言(源语言)编写的程序,并把该程序翻译成为一个等价的、用另一种语言(目标语言)编写的程序。
解释器:并不通过翻译的方式生成目标程序,直接利用用户提供的输入执行源程序中指定的操作。

编译器 解释器
执行速度更快 错误诊断效果更好

创建可执行的目标执行还需要的其他程序,比如
预处理器:把分割为多个模块的源程序聚合在一起。
汇编器:生成可重定位的机器代码。
加载器:把所有的可执行目标文件放到内存中执行。

编译器

词法分析

词法分析器读取组成源程序的字符流,并且将它们组织成为有意义的词素的序列。
对于每个词素,词法分析器产生如下形式的词法单元(token)作为输出。
(token-name, attribute-value)

比如:
position = initial + rate * 60

  • position是一个词素,被映射成(id,1),其中id表示标识符的抽象符号,而1指向符号表中position对应的条目。
  • =是一个词素,被映射成(=),因为不需要属性值,所以忽略第二个分量。
  • initial是一个词素,被映射成(id,2)
  • +是一个词素,被映射成(+)
  • rate是一个词素,被映射成(id,3)
  • 是一个词素,被映射成()
  • 60是一个词素,被映射成(60)
    (id, 1)(=)(id,2)(+)(id,3)(*)(60)

符号表

符号表数据结构为每个变量名字创建了一个记录条目。记录的字段就是名字的各个属性,包括存储分配、类型、作用域等,过程名字包括参数数量和类型、传递方法(传值或传引用)、返回类型等。
|1|position|…|
|-|–|–|
|2|initial|…|
|3|rate|…|
||||

语法分析

语法分析器使用由词法分析器生成的各个词法单元的第一个分量来创建树形的中间表示。一个常用的表示方法是语法树,树中的每个内部节点表示一个运算,而该节点的子节点表示该运算的分量。

语义分析

使用语法书和符号表中的信息来检查源程序是否和语言定义的语义一直。它同时也收集类型信息,并把这些信息存放在语法树或符号表中,以便在随后的中间代码生成过程中使用。
语义分析的一个重要部分是类型检查。

中间代码生成

在把一个源程序翻译成目标代码的过程中,一个编译器可能构造出一个或多个中间表示。这些中间表示可以有多种形式。语法树是一种中间表示形式。

代码优化

机器无关的代码优化步骤试图改进中间代码,以便生成更好的目标代码。

代码生成

代码生成器以源程序的中间表示形式作为输入,并把它映射到目标语言。

1…101112…22

Paul Jiang

212 日志
26 分类
26 标签
Links
  • 褚霸
  • 章亦春
  • Martin Fowler
© 2020 Paul Jiang
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Gemini v6.5.0