Hexo

今天又是充满希望的一天

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

面向微服务的体系结构如今风靡全球。这是因为更快的部署节奏和更低的成本是面向微服务的体系结构的基本承诺。

然而,对于大多数试水的公司来说,开发活动更多的是将现有的单块应用程序转换为面向微服务的体系结构,这可能是许多层面上阻碍和冲突的根源。

虽然Greenfield (未开发的)面向微服务的体系结构实现可以坚持对当前微服务的严格解释-设计原则。但在面向微服务的体系结构中,分解的遗留应用程序存在灰色阴影,如果没有其他原因,只能满足预算和时间限制。

在企业管理链的某个地方,有一位业务主管在一个面向微服务的体系结构中查看与这些遗留应用程序相关的分解成本,并将其与遗留代码已经提供的价值进行比较。一旦开发成本超过了预期的收益,业务主管很可能会退出并取消该项目。

这种事经常会发生。

因此,开发经理面临着巨大的压力,要求他们尽快将代码输出。“足够好”地成为转型的理想目标。

现在,这不一定是一件坏事。与等待梦想到来相比,输出工作代码的能力总是更好。但是,“灰色的阴影”是很难管理的,问题就在于如何界定“足够好”的界限。

因此,冲突开始了。一方想要输出他们想要的东西,而另一方则希望做更多的改进。

对你来说,挑战是不要让这些不同学派在本质上是信仰支持的观点上制造一场没完没了的争吵。如果您这样做了,它将造成一种情况,即根本不提供任何代码。现在,冲突可以从许多相互竞争的想法中综合出最好的想法。但是,当话语退化为永无止境的冲突时,它可能是致命的。

我通过集中讨论以下三个问题来处理这类情况,以避免这种冲突:

  • 设计的理由是什么?
  • 风险有多大?
  • 减少风险的计划是什么?

请允许我详细说明。

1. 设计的理由是什么?

当您评估面向微服务的体系结构的设计时,所面临的挑战是将过去的观点转移到理论基础分析上。它的创建主要来自于单个应用程序的分解。任何设计都可能“足够好”,只要你能证明它的好处和价值。

例如,面向微服务的体系结构设计的首选样式之一是采用事件驱动的方法进行服务间通信。具体来说,这意味着您使用消息节点以异步方式在微服务之间传递消息。然而,从长远来看,虽然异步通信更加灵活和可扩展,但消息系统实现比在“面向”微服务的API之间使用同步HTTP调用的设计要复杂得多。因此,当市场时间被关注时,完全有理由将单块应用程序中的特性重构为以HTTP API方式表示的独立的微服务。

Synchronous microservices are usually less complex to implement than asynchronous ones.

与异步服务相比,同步微服务的实现通常不那么复杂。

从长远来看,同步通信不一定是最佳选择,但考虑到从单块应用程序中提取独立的微服务所需的所有其他工作,同步对于第一个版本来说是“足够好”的。因此,这是一个合理的理由。

然而,这并不是说同步方法没有风险。事实上,风险有很多。当涉及到审查面向微服务的体系结构设计时,仅仅说明理由并不是唯一的因素。风险也必须加以阐述。

2. 风险有多大?

所有的设计都有内在的风险。在上面描述的同步设计示例中,这种服务间通信方法可能会导致服务之间类型耦合的风险,由于同步HTTP通信和其他通信的性质而增加延迟增加延迟。

重要的是要让人们知道这些风险,这样就可以根据预期设计的合理性来权衡它们。如果风险是巨大的,再多的理由也是不够的。另一方面,考虑到目前的需求,某些风险可能是可以接受的。诀窍是确保风险在审查过程中得到明确的传达。讨论中已知的风险总是比隐藏的风险更可取,而这种风险可能会在路上造成冲击。此外,如果您以前知道风险,那么随着面向微服务的体系结构的成熟,您可以计划如何在未来的版本中更好地向前迈进。这就是减少风险的原因。

3. 减少风险的计划是什么?

一个明智的应用程序设计人员的一个标志是能够识别他们的设计风险,一旦确定下来他会有远见地阐明一种方法,以减轻这些风险。没有适当的缓解技术的风险识别是思维不完整的标志。

如果面向微服务的体系结构设计有很大的风险和解决这些问题的边际计划,那么设计团队需要认真考虑其可行性。此外,如果缓解计划不切实际-超出项目的专门知识和预算-设计的可行性也需要质疑。这都是平衡的问题。

一个平衡良好的面向微服务的体系结构设计是合理的,因为它想要满足的条件与其固有的设计风险和旨在解决这些风险的缓解计划相权衡。

4. 把它们放在一起

冲突是创造性进程的重要组成部分。有创造力的人往往对自己的想法坚韧不拔。所以,当你把它们放在一个房间里,让他们为面向微服务的建筑设计一个单一的设计时,紧张关系肯定会加剧。事情就是这样的。但要振作起来!冲突是好事。

幸运的是,有了一种理性的方法,用我前面描述的三个问题来审查面向微服务的体系结构设计,您就可以促进客观的讨论,从而产生软件以及时满足您的需求。没有任何设计是完美的,特别是那些分解单个应用程序的设计。但是,交付面向微服务的体系结构有一个很大的好处,这个体系结构足够好有效运作在短期和灵活性足够持续不断改善长期。

原文:https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/3-questions-to-ask-in-a-microservices-oriented-architecture-review

作者:Bob Reselman

译者:遗失的拂晓

Java 8曾经与Docker无法很好地兼容性,现在问题已消失。

请注意:我在本文中使用采用GNU GPL v2许可证的OpenJDK官方docker映像。在Oracle Java SE中,这里描述的docker支持功能在更新191中引入。Oracle在2019年4月更改了Java 8更新的许可证,自Java SE 8 Update 211以来商业使用不再免费。

你是否遇到过在docker中运行的基于JVM的应用程序出现“随机”故障?或者也许是一些奇怪的死机?两者都可能是Java 8(仍广泛使用的)中糟糕的docker支持引起的。

Docker使用控制组(cgroups)来限制资源。在容器中运行应用程序时限制内存和CPU绝对是个好主意――它可以阻止应用程序占用整个可用内存及/或CPU,这会导致在同一个系统上运行的其他容器毫无反应。限制资源可提高应用程序的可靠性和稳定性。它还允许为硬件容量作好规划。在Kubernetes或DC/OS之类的编排系统上运行容器时尤为重要。

问题

JVM可以“看到”系统上的整个内存和可用的所有CPU核心,并确保与资源一致。它默认情况下将最大堆大小(heap size)设置为系统内存的1/4,并将某些线程池大小(比如针对GC)设置为物理核心数量。不妨举例说明。

我们将运行一个简单的应用程序,它消耗尽可能多的内存(可在该网站上找到):

img

我们在拥有64GB内存的系统上运行,所以不妨检查默认的最大堆大小:

如上所述,它是物理内存的1/4即16GB。如果我们使用docker cgroups限制内存,会发生什么?不妨检查一下:

img

JVM进程被杀死了。由于它是一个子进程――容器本身幸存下来,但通常当java是容器(PID 1)内的唯一进程时,容器会崩溃。

不妨深入看看系统日志:

img

img

像这样的故障调试起来可能很难――应用程序日志中没有任何内容。在AWS ECS之类的托管系统上尤其困难重重。

CPU怎么样?不妨再次检查,运行一个显示可用处理器数量的小程序:

不妨在一个cpu编号设置为1的docker容器中运行它:

不好,这个系统上的确有12个CPU。因此,即使可用处理器的数量限制为1,JVM也会尝试使用12――比如说,GC线程数量由该公式设置:

在拥有N个硬件线程(N大于8)的机器上,并行收集器使用N的固定分数作为垃圾收集器线程的数量。如果N的值很大,该分数约5/8。如果N的值低于8,使用的数字是N。

在我们的情况下:

解决方案

OK,我们现在意识到了这个问题。有解决方案吗?幸运的是,有!

新的Java版本(10及以上版本)已经内置了docker支持功能。但有时升级不是办法,比如说如果应用程序与新JVM不兼容就不行。

好消息:Docker支持还被向后移植到Java 8。不妨检查标记为8u212的最新openjdk映像。我们将内存限制为1G,并使用1个CPU:docker run -ti –cpus 1 -m 1G openjdk:8u212-jdk。

内存:

它是256M,正好是已分配内存的1/4。

CPU:

正如我们想要的那样。

此外,还有几个新的设置:

它们允许微调堆大小――这些设置的含义在StackOverflow的这个优秀答案中已得到了解释。请注意:他们设置的是百分比,而不是固定值。正因为如此,改变Docker内存设置不会破坏任何东西。

如果由于某种原因不想要看到新的JVM行为,可以使用-XX:-UseContainerSupport来关闭。

总结

为基于JVM的应用程序设置正确的堆大小极其重要。如果使用最新的Java 8版本,你可以依赖安全(但非常保守)的默认设置。不需要在docker入口点中使用任何变通办法,也不需要再将Xmx设置为固定值。

使用JVM愉快!

微服务高可用方案

一、微服务的高可用

在注册中心、配置中心高可用方案之前,了解一下注册中心的工作原理,下面分为两个部分来解释,一是注册中心和各个微服务的注册表的获取与同步,二是注册中心如何去维护注册表。

1.1、注册表的获取与同步

Eureka Server和Eureka Client之间的关系,通过注册表来维护,而注册表的通过Eureka Server集中化管理,每个Client在本地进行注册表的缓存,通过周期性的任务拉取最新的注册表信息。简单的示例图如下。

img

根据上图所展示的流程,可以了解到注册中心与微服务之间的基本联系的流程:

1.服务A启动时,向Eureka Server注册自己的相关信息

2.当服务B向Eureka Server拉取最新的注册表时,就可以拿到服务A的一台机器注册信息

3.服务A的另外两台机器再去注册,服务B 30s后再次去拉取时,就会得到服务A的三台机器的注册信息

4.服务A、每30s向Eureka Server发送一次心跳信息,表明自己的注册信息还是有效的

以上是注册中心与微服务之间交互的大体流程,在具体的实践中,Eureka Server会提供多级缓存,其中的注册表的信息的获取与同步,又会有细微的差别。

1.Eureka Server的注册表直接基于纯内存,即在内存里维护了一个数据结构。

2.各个服务的注册、服务下线、服务故障,全部会在内存里维护和更新这个注册表。

3.各个服务每隔30秒拉取注册表的时候,Eureka Server就是直接提供内存里存储的有变化的注册表数据给他们就可以了。

4.同样,每隔30秒发起心跳时,也是在这个纯内存的Map数据结构里更新心跳时间。

Eureka Server的注册表是纯内存处理的,因此处理速度会很快,同时提供 readWriteCacheMap 和 readOnlyCacheMap 做缓存,保障了频繁读写不会冲突。示意图如下。

img

上图介绍了Eureka Server多级缓存的工作原理:

1.当第一台服务A注册时,它的注册信息会更新到内存的注册表中,如果 readWriteCacheMap 中有相应的信息,则过期掉,如果没有则不做操作

2.当服务B去拉取注册表信息时,先找 readOnlyCacheMap ,没有再找 readWriteCacheMap ,再没有就去内存的注册表查找注册信息,查到就更新到 readWriteCacheMap 中,返回给服务B,服务B的注册表中,就会有一台服务A的机器注册信息

3.readOnlyCacheMap 和 readWriteCacheMap 之间的同步是有一个后台的定时任务,每隔30s去同步一次,缓存同步任务

4.第二台服务A注册时,更新内存的注册表,同时把 readWriteCacheMap 过期掉

5.在缓存同步任务执行之前服务B去拉取注册表时,都是从 readOnlyCacheMap 中拿到数据,新的注册表的信息,不会被服务B拿到

6.30s后,缓存同步任务会同步 readWriteCacheMap 和 readOnlyCacheMap 中的数据,把readOnlyCacheMap 中的注册表过期掉,这时服务B就会找 readWriteCacheMap 拿数据,readWriteCacheMap 从内存中拿到数据后缓存,返回给服务B,服务B的注册表中,就会有两台服务A的机器注册信息

7.在下一个30s,缓存同步任务把 readWriteCacheMap 同步到 readOnlyCacheMap 之前, readOnlyCacheMap 没有第二台服务A的注册缓存,因此都是从 readWriteCacheMap 中取到最新数据

注:

readOnlyCacheMap 缓存更新的定时器时间间隔,默认为30秒

readWriteCacheMap 缓存过期时间,默认为 180 秒

由以上流程说明可知,Eureka Server采取了多级缓存策略,同时最新的注册表生效有30s的时延。多级缓存机制的优点是什么:

1.尽可能保证了内存注册表数据不会出现频繁的读写冲突问题。

2.并且进一步保证对Eureka Server的大量请求,都是快速从纯内存走,性能极高。

1.2、注册中心维护微服务的注册表

Eureka Client与注册表相关的行为如下所示:

1.服务注册(Registry)——初始化时执行一次,向服务端注册自己服务实例节点信息包括ip、端口、实例名等,基于POST请求。

2.服务续约(renew)——默认每隔30s向服务端PUT一次,保证当前服务节点状态信息实时更新,不被服务端失效剔除。

3.更新已经注册服务列表(fetchRegistry)——默认每隔30s从服务端GET一次增量版本信息,然后和本地比较并合并,保证本地能获取到其他节点最新注册信息。

4.服务下线(cancel)——在服务shutdown的时候,需要及时通知服务端把自己剔除,以避免客户端调用已经下线的服务。

Eureka Client是通过Jersey Client基于Http协议与Eureka Server交互来注册服务、续约服务、取消服务、服务查询等。同时,Server端还会维护一份服务实例清单,并每隔90s对未续约的实例进行失效剔除。

Eureka Server有一个自我保护机制,当网络发生故障时,客户端与服务端不通,这是需要启动Eureka Server的自我保护机制,这样不会剔除服务,当网络恢复时,退出自我保护。自我保护有两个参数,最后一分钟收到的心跳数(Renews (last min))、期望收到的心跳数(Renews threshold),当Renews threshold > Renews (last min) 时,进入自我保护模式。

Renews (last min) = 实例数 * 2 #实例数算上Eureka Server自注册服务

Renews threshold = Renews (last min) * 0.85 # 0.85可配置

下图的注册中有10个实例:

img

推荐多个Eureka Server部署时,开启自我保护

1
eureka.client.register-with-eureka = true

1.3、分布式注册中心

了解了注册中心的工作原理,下面开始研究分布式服务,多注册中心、多服务实例的情况。

当微服务仅向一台注册中心注册时,当这个注册中心发生故障时,新服务无法继续注册上去,旧服务的注册信息,缓存在其他注册中心和客户端中,依旧可以使用,当重启之后,无法向注册中心注册,也是无法使用的。

因此构建高可用的注册中心时,需要交叉注册,每个注册中心既当服务端,又当客户端,向其他注册中心注册自己,同时微服务需要向每个注册中心进行注册,由注册中心自己过滤互备,防止单个注册中心故障而导致只往它上面注册微服务重启后不可用。示意图如下所示。

img

目前注册中心与配置中心集中在一起,可拆可不拆,对整体影响不大,拆分是为了注册中心和配置中心相互间不影响。gitlab部署在某一台机器上,所有config共用,由于gitlab的原因,导致config的分布式存在单点故障的隐患。每个config分别用独立的gitlab,又给运维带来极大的不便。后期采用apollo,用数据库存储配置,利用数据库的分布式优势替代gitlab,来解决单点故障的问题。

1.4、注册中心压测

根据压测调研,8核4G的Eureka Server在处理1000个服务实例时,没有任何压力,在默认情况下,可以处理7000个实例,超出的会超时报错,在修改tomcat的配置之后,最多可以承载8000实例,此时CPU基本满载。

升级注意事项:

1、Eureka Server之间相互注册,Eureka Client需要在每个Server上都注册一边

2、Eureka Server开启自我保护

3、Eureka Client的实例数不超过1000个

参考:

[1] https://www.jianshu.com/p/ae4f0c8b8135

[2] https://www.cnblogs.com/xishuai/p/spring-cloud-eureka-safe.html

[3] http://springcloud.cn/view/31

1、排查

CentOS 7.3 默认安装的是python2,使用命令 python -V 可以看到 python 的版本

1
Python 2.7.5

然后使用命令 which python 查看一下Python可执行文件的位置

可见执行文件在 /usr/bin/ 目录下,切换到该目录下执行 ll python/* 命令查看 

可见 python 和 python2 都指向了 python2.7,因此执行 python 程序可用 python 和 python2。

我们在 yum 仓库搜索 python 相关的包,发现没有 python3 相关的

1
2
yum search python
yum search python3

因此,我们使用编译的形式安装 python3,并使用软链在 /usr/bin/ 下建立 python3 。

2、安装

没有 python 的可用 yum install python 来安装

安装相关依赖

1
yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make

下载 python3 的包,在任意目录。没有wget的,yum安装一下,再下载

1
wget https://www.python.org/ftp/python/3.6.2/Python-3.6.2.tar.xz

解压

1
tar -xvJf  Python-3.6.2.tar.xz

切换进入

1
cd Python-3.6.2

编译安装

1
2
./configure prefix=/usr/local/python3
make && make install

安装完毕,/usr/local/ 目录下就会有 python3了

因此我们可以添加软链到执行目录下/usr/bin

1
ln -s /usr/local/python3/bin/python3 /usr/bin/python3

查看 python 的版本

1
2
3
python -V
python2 -V
python3 -V

需要 python 即是 python3 的,可以参考最后的参考链接。建软链不同,和需要修改 yum 的配置(不建议去修改 python 的默认版本,建议用 python3 来代替,因为有些软件默认是 python2 的,可能会引起意外的事故)

参考:
https://www.cnblogs.com/JahanGu/p/7452527.html

先看java -version

1
2
3
$java version "1.8.0_111"
Java(TM) SE Runtime Environment (build 1.8.0_111-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.111-b14, mixed mode)

然后:

1
echo $JAVA_HOME

不一定有,如果没有,那就要找一下 先

1
2
$which java
/usr/bin/java

再找到/usr/bin/java的超链接位置发现还是超链接

1
2
$ls -lrt /usr/bin/java
lrwxrwxrwx 1 root root 22 Jul 27 11:43 /usr/bin/java -> /etc/alternatives/java

再来一次,发现最终位置

1
2
$ls -lrt /etc/alternatives/java
lrwxrwxrwx 1 root root 35 Jul 27 11:43 /etc/alternatives/java -> /usr/java/jdk1.8.0_111/jre/bin/java

最后的这个jdk位置就是目前用的java的jdk位置

1
/usr/java/jdk1.8.0_111/(这个是我的,你用你自己的)

在.bashrc里面
加上一句
export JAVA_HOME=你的java安装路径

getFileMagic() only operates on streams which support mark(int)

使用 bis 解决

1
2
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(path)));
XSSFWorkbook xssfWorkbook = new XSSFWorkbook(bis);

找不到类,一般是版本问题,可以把版本调整一致,3.15
java.lang.reflect.InvocationTargetException
org.apache.poi.POIXMLException: java.lang.reflect.InvocationTargetException

Java.lang.NoClassDefFoundError :org/openxmlformats/schemas/spreadsheetml/x2006/main/ctextensionlist

pom配置,版本保持一致,3.15

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.15</version>
</dependency>

用过 Spring 的 @EnableScheduling 的都知道,有三种方式,即 @Scheduled 注解的 fixedRate(fixedRateString), fixedDelay(fixedDelayString), 以及 cron
这里讨论 fixedRate 和 fixedDelay 参数

fixedRate 是有一个时刻表的概念,在任务启动时,T1、T2、T3就已经排好了执行的时刻,比如1分、2分、3分,当T1的执行时间大于1分钟时,就会造成T2晚点,当T1执行完时T2立即执行,

fixedDelay 比较简单,表示上个任务结束,到下个任务开始的时间间隔。无论任务执行花费多少时间,两个任务间的间隔始终是一致的。

0%