你若安好便是晴天

你若安好便是晴天 查看完整档案

上海编辑郑州大学  |  生物技术 编辑夏雨  |  java工程师 编辑填写个人主网站
编辑

做了这么久的java开发了,日常开发中使用的技术想找个地儿记录一下,一是为了帮助我记忆,二是通过这些文章知道自己的薄弱点。

个人动态

你若安好便是晴天 发布了文章 · 8月10日

分布式选举算法

1.分布式基本理论

  • cap理论:
     
    consistency: 一致性
     
    availablity:可用性
     
    partition tolerance: 分区容错
     
    分布式服务在系统出现一定故障的时候,还可以对外提供服务。基本所有分布式系统都可以满足此要求。c和a之间存在矛盾,如果追求一致性就需要抛弃可用性,如果追求可用性,数据一致性就不会被满足。
     
  • base理论:
     
    basic available:基本可用
     
    softstatue: 软状态
     
    eventually: 最终一致性
     
    系统可以处于一致性中间状态,但可用,系统可以达到最终一致性状态,其实现方法可以使用基于消息中间件+定时任务补发消息来实现。
     

2.分布式一致性算法

 
    在cap理论中,如果保证数据强一致性,就需要所有副本同步主节点的所有数据,在数据同步过程中,对外不可用,在所有分布式一致性算法中,解决此的思想是大多数副本同步(一般都是过半)主节点的数据后,主节点就可以使用对客户端进行相应。
 

  • basic paxos算法
     
    basic paxos算法有prepare、accept和learn三个阶段,以及提案者,决策者和学习者。
     
    prepare阶段:提案者发出请求到过半的决策者,请求中携带生成的全局唯一递增id,该id可以是时间戳+机器id。
     
    accept阶段:决策者接收到提案者的id信息后,通过比较保存的id和请求的id大小,如果请求的id大于保存的id,就会把保存中的最大的id及对应的提案值作为响应发送给提案者。而且之后决策者会拒绝所有id小于当前的id的提案请求,只接受大于当前id的请求。当提案者接收到决策者过半的响应后,就会在所有响应中获取最大id的提案值作为提案值,发送给过半决策者,此过半决策者可以与提案决策者不同,决策者收到请求后,就会提交请求。
     
    learn阶段:所有learner同步决策者的数据。具体方式可以是每一个learner去请求accept,也可以所有learner选择一个主learner,主learner同步accept的数据,其他learner同步主learner。

     
    上述paxos算法是basic paxos算法,如果有多个提案者,就会导致活锁问题。为了解决此问题,提出只有一个主提案者,在多个提案者中,先选择一个主提案者,所有的提案都由该主提案者进行提交。
     

  • Raft算法
     
    leader选举
     
    日志同步
     
  • zab算法
     

    • 启动选举leader
      每台机器发送(myid,zxid)到别的机器,别的机器检查是否是在选举过程中,然后比较zxid,投票给zxid大的,如果zxid相同就投票给myid大的,等到有机器接收到一半以上的投票后即成为leader

 

  • 运行中选举
    逻辑大致同启动时选举leader,只不过换成sid和zxid,过半投票机制。然后再同步数据。

 

  • kafka 选举
     
    isr列表
     
    高水位HW
     
    过半机制
查看原文

赞 0 收藏 0 评论 0

你若安好便是晴天 发布了文章 · 8月7日

jvm性能分析工具

1.jconsole

public class TestGC {
    public static void main(String[] args) throws InterruptedException {
        int size = 1024 * 1024;
        byte[] data = new byte[size];
        data = null;
        System.gc();
        while (true){
            System.out.println("true");
            Thread.sleep(1000);
        }
    }
}

编译及运行

[root@dev1 java]# javac TestGC.java 
[root@dev1 java]# java -Dcom.sun.management.jmxremote.port=9890 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=192.168.1.100 TestGC

输入目标主机域名及上述端口,用户名/口令,点击连接即可
image.png

2.visualvm

下载visualvm到本地,打开visualvm
image.png

输入主机名
image.png

输入用户名及密码,端口号
image.png

即可查看jvm信息
image.png

3.jinfo工具

查看jvm进程参数等信息

[root@dev1 ~]# jps
13507 -- process information unavailable
3492 TestGC
13398 -- process information unavailable
13642 -- process information unavailable
3851 Jps
[root@dev1 ~]# jinfo 3492
Java System Properties:
#Fri Aug 07 01:37:04 CST 2020
awt.toolkit=sun.awt.X11.XToolkit
java.specification.version=11
sun.cpu.isalist=
sun.jnu.encoding=UTF-8
com.sun.management.jmxremote.authenticate=false
java.class.path=.
java.vm.vendor=Oracle Corporation
sun.arch.data.model=64
java.vendor.url=http\://java.oracle.com/
user.timezone=
java.vm.specification.version=11
os.name=Linux
sun.java.launcher=SUN_STANDARD
user.country=CN
sun.boot.library.path=/usr/lib/jvm/java-11-openjdk-11.0.7.10-4.el7_8.x86_64/lib
com.sun.management.jmxremote.ssl=false
sun.java.command=TestGC
jdk.debug=release
sun.cpu.endian=little
user.home=/root
user.language=zh
java.specification.vendor=Oracle Corporation
java.version.date=2020-04-14
java.home=/usr/lib/jvm/java-11-openjdk-11.0.7.10-4.el7_8.x86_64
file.separator=/
java.vm.compressedOopsMode=32-bit
line.separator=\n
java.specification.name=Java Platform API Specification
java.vm.specification.vendor=Oracle Corporation
java.awt.graphicsenv=sun.awt.X11GraphicsEnvironment
sun.management.compiler=HotSpot 64-Bit Tiered Compilers
java.runtime.version=11.0.7+10-LTS
user.name=root
path.separator=\:
os.version=3.10.0-1062.el7.x86_64
java.runtime.name=OpenJDK Runtime Environment
file.encoding=UTF-8
java.vm.name=OpenJDK 64-Bit Server VM
java.vendor.version=18.9
java.vendor.url.bug=http\://bugreport.java.com/bugreport/
java.io.tmpdir=/tmp
java.version=11.0.7
com.sun.management.jmxremote.port=9890
user.dir=/data/java
os.arch=amd64
java.vm.specification.name=Java Virtual Machine Specification
java.awt.printerjob=sun.print.PSPrinterJob
sun.os.patch.level=unknown
java.library.path=/usr/java/packages/lib\:/usr/lib64\:/lib64\:/lib\:/usr/lib
java.vendor=Oracle Corporation
java.vm.info=mixed mode, sharing
java.vm.version=11.0.7+10-LTS
java.rmi.server.hostname=192.168.1.100
java.rmi.server.randomIDs=true
sun.io.unicode.encoding=UnicodeLittle
java.class.version=55.0
VM Flags:
-XX:CICompilerCount=2 -XX:ConcGCThreads=1 -XX:G1ConcRefinementThreads=2 -XX:G1HeapRegionSize=1048576 -XX:GCDrainStackTargetSize=64 -XX:InitialHeapSize=62914560 -XX:+ManagementServer -XX:MarkStackSize=4194304 -XX:MaxHeapSize=994050048 -XX:MaxNewSize=595591168 -XX:MinHeapDeltaBytes=1048576 -XX:NonNMethodCodeHeapSize=5825164 -XX:NonProfiledCodeHeapSize=122916538 -XX:ProfiledCodeHeapSize=122916538 -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseG1GC 
VM Arguments:
jvm_args: -Dcom.sun.management.jmxremote.port=9890 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=192.168.1.100 
java_command: TestGC
java_class_path (initial): .
Launcher Type: SUN_STANDARD
[root@dev1 ~]

4.查看进程中线程堆栈信息

[root@dev1 ~]# jstack 3492
2020-08-07 01:38:30
Full thread dump OpenJDK 64-Bit Server VM (11.0.7+10-LTS mixed mode, sharing):
Threads class SMR info:
_java_thread_list=0x00007fe12c0030c0, length=13, elements={
0x00007fe174015000, 0x00007fe1740b3800, 0x00007fe1740b5800, 0x00007fe1740bb000,
0x00007fe1740bd000, 0x00007fe1740bf800, 0x00007fe1740c1800, 0x00007fe1740df000,
0x00007fe17410c800, 0x00007fe17423a800, 0x00007fe174241800, 0x00007fe174255800,
0x00007fe12c001800
}
"main" #1 prio=5 os_prio=0 cpu=480.16ms elapsed=106.98s tid=0x00007fe174015000 nid=0xda5 waiting on condition  [0x00007fe17d14a000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(java.base@11.0.7/Native Method)
        at TestGC.main(TestGC.java:9)
"Reference Handler" #2 daemon prio=10 os_prio=0 cpu=0.64ms elapsed=106.97s tid=0x00007fe1740b3800 nid=0xdac waiting on condition  [0x00007fe15c78b000]
   java.lang.Thread.State: RUNNABLE
        at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.7/Native Method)
        at java.lang.ref.Reference.processPendingReferences(java.base@11.0.7/Reference.java:241)
        at java.lang.ref.Reference$ReferenceHandler.run(java.base@11.0.7/Reference.java:213)
"Finalizer" #3 daemon prio=8 os_prio=0 cpu=0.26ms elapsed=106.97s tid=0x00007fe1740b5800 nid=0xdad in Object.wait()  [0x00007fe15c68a000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(java.base@11.0.7/Native Method)
        - waiting on <0x00000000c4c7ccc8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(java.base@11.0.7/ReferenceQueue.java:155)
        - waiting to re-lock in wait() <0x00000000c4c7ccc8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(java.base@11.0.7/ReferenceQueue.java:176)
        at java.lang.ref.Finalizer$FinalizerThread.run(java.base@11.0.7/Finalizer.java:170)
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 cpu=0.24ms elapsed=106.96s tid=0x00007fe1740bb000 nid=0xdae runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 cpu=288.06ms elapsed=106.96s tid=0x00007fe1740bd000 nid=0xdaf waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task
"C1 CompilerThread0" #6 daemon prio=9 os_prio=0 cpu=243.13ms elapsed=106.96s tid=0x00007fe1740bf800 nid=0xdb0 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task
"Sweeper thread" #7 daemon prio=9 os_prio=0 cpu=2.62ms elapsed=106.96s tid=0x00007fe1740c1800 nid=0xdb1 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
"Common-Cleaner" #8 daemon prio=8 os_prio=0 cpu=0.56ms elapsed=106.93s tid=0x00007fe1740df000 nid=0xdb2 in Object.wait()  [0x00007fe15c185000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(java.base@11.0.7/Native Method)
        - waiting on <0x00000000c4cafc78> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(java.base@11.0.7/ReferenceQueue.java:155)
        - waiting to re-lock in wait() <0x00000000c4cafc78> (a java.lang.ref.ReferenceQueue$Lock)
        at jdk.internal.ref.CleanerImpl.run(java.base@11.0.7/CleanerImpl.java:148)
        at java.lang.Thread.run(java.base@11.0.7/Thread.java:834)
        at jdk.internal.misc.InnocuousThread.run(java.base@11.0.7/InnocuousThread.java:134)
"Service Thread" #9 daemon prio=9 os_prio=0 cpu=0.16ms elapsed=106.89s tid=0x00007fe17410c800 nid=0xdb3 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
"RMI TCP Accept-0" #11 daemon prio=5 os_prio=0 cpu=0.48ms elapsed=106.52s tid=0x00007fe17423a800 nid=0xdce runnable  [0x00007fe15415d000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(java.base@11.0.7/Native Method)
        at java.net.AbstractPlainSocketImpl.accept(java.base@11.0.7/AbstractPlainSocketImpl.java:458)
        at java.net.ServerSocket.implAccept(java.base@11.0.7/ServerSocket.java:565)
        at java.net.ServerSocket.accept(java.base@11.0.7/ServerSocket.java:533)
        at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(java.rmi@11.0.7/TCPTransport.java:394)
        at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(java.rmi@11.0.7/TCPTransport.java:366)
        at java.lang.Thread.run(java.base@11.0.7/Thread.java:834)
"RMI TCP Accept-9890" #12 daemon prio=5 os_prio=0 cpu=0.11ms elapsed=106.51s tid=0x00007fe174241800 nid=0xdcf runnable  [0x00007fe131082000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(java.base@11.0.7/Native Method)
        at java.net.AbstractPlainSocketImpl.accept(java.base@11.0.7/AbstractPlainSocketImpl.java:458)
        at java.net.ServerSocket.implAccept(java.base@11.0.7/ServerSocket.java:565)
        at java.net.ServerSocket.accept(java.base@11.0.7/ServerSocket.java:533)
        at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(java.rmi@11.0.7/TCPTransport.java:394)
        at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(java.rmi@11.0.7/TCPTransport.java:366)
        at java.lang.Thread.run(java.base@11.0.7/Thread.java:834)
"RMI TCP Accept-0" #13 daemon prio=5 os_prio=0 cpu=0.21ms elapsed=106.46s tid=0x00007fe174255800 nid=0xdd0 runnable  [0x00007fe130f81000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(java.base@11.0.7/Native Method)
        at java.net.AbstractPlainSocketImpl.accept(java.base@11.0.7/AbstractPlainSocketImpl.java:458)
        at java.net.ServerSocket.implAccept(java.base@11.0.7/ServerSocket.java:565)
        at java.net.ServerSocket.accept(java.base@11.0.7/ServerSocket.java:533)
        at sun.management.jmxremote.LocalRMIServerSocketFactory$1.accept(jdk.management.agent@11.0.7/LocalRMIServerSocketFactory.java:52)
        at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.executeAcceptLoop(java.rmi@11.0.7/TCPTransport.java:394)
        at sun.rmi.transport.tcp.TCPTransport$AcceptLoop.run(java.rmi@11.0.7/TCPTransport.java:366)
        at java.lang.Thread.run(java.base@11.0.7/Thread.java:834)
"Attach Listener" #14 daemon prio=9 os_prio=0 cpu=26.57ms elapsed=86.00s tid=0x00007fe12c001800 nid=0x1067 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
"VM Thread" os_prio=0 cpu=11.22ms elapsed=106.97s tid=0x00007fe1740b0800 nid=0xdab runnable  
"GC Thread#0" os_prio=0 cpu=11.52ms elapsed=106.98s tid=0x00007fe17402d000 nid=0xda6 runnable  
"GC Thread#1" os_prio=0 cpu=8.66ms elapsed=106.44s tid=0x00007fe144001000 nid=0xdd2 runnable  
"G1 Main Marker" os_prio=0 cpu=0.36ms elapsed=106.98s tid=0x00007fe174047000 nid=0xda7 runnable  
"G1 Conc#0" os_prio=0 cpu=0.07ms elapsed=106.98s tid=0x00007fe174049000 nid=0xda8 runnable  
"G1 Refine#0" os_prio=0 cpu=0.23ms elapsed=106.98s tid=0x00007fe174082800 nid=0xda9 runnable  
"G1 Young RemSet Sampling" os_prio=0 cpu=16.03ms elapsed=106.98s tid=0x00007fe174084800 nid=0xdaa runnable  
"VM Periodic Task Thread" os_prio=0 cpu=81.97ms elapsed=106.45s tid=0x00007fe174258000 nid=0xdd1 waiting on condition  
JNI global refs: 15, weak refs: 0
[root@dev1]
查看原文

赞 0 收藏 0 评论 0

你若安好便是晴天 赞了文章 · 8月7日

如何合理的规划jvm性能调优

JVM性能调优涉及到方方面面的取舍,往往是牵一发而动全身,需要全盘考虑各方面的影响。但也有一些基础的理论和原则,理解这些理论并遵循这些原则会让你的性能调优任务将会更加轻松。为了更好的理解本篇所介绍的内容。你需要已经了解和遵循以下内容:

1、已了解jvm 垃圾收集器

2、已了解jvm 性能监控常用工具

3、能够读懂gc日志

4、确信不为了调优而调优,jvm调优不能解决一切性能问题

如果对这些不了解不建议读本篇文章。

本篇文章基于jvm性能调优,结合jvm的各项参数对应用程序调优,主要内容有以下几个方面:

1、jvm调优的一般流程

2、jvm调优所要关注的几个性能指标

3、jvm调优需要掌握的一些原则

4、调优策略&示例

一、性能调优的层次

为了提升系统性能,我们需要对系统的各个角度和层次来进行优化,以下是需要优化的几个层次。

从上面我们可以看到,除了jvm调优以外,还有其他几个层面需要来处理,所以针对系统的调优不是只有jvm调优一项,而是需要针对系统来整体调优,才能提升系统的性能。本篇只针对jvm调优来讲解,其他几个方面,后续再介绍。

在进行jvm调优之前,我们假设项目的架构调优和代码调优已经进行过或者是针对当前项目是最优的。这两个是jvm调优的基础,并且架构调优是对系统影响最大的 ,我们不能指望一个系统架构有缺陷或者代码层次优化没有穷尽的应用,通过jvm调优令其达到一个质的飞跃,这是不可能的。

另外,在调优之前,必须得有明确的性能优化目标, 然后找到其性能瓶颈。之后针对瓶颈的优化,还需要对应用进行压力和基准测试,通过各种监控和统计工具,确认调优后的应用是否已经达到相关目标。

二、jvm调优流程

调优的最终目的都是为了令应用程序使用最小的硬件消耗来承载更大的吞吐。jvm的调优也不例外,jvm调优主要是针对垃圾收集器的收集性能优化,令运行在虚拟机上的应用能够使用更少的内存以及延迟获取更大的吞吐量。当然这里的最少是最优的选择,而不是越少越好。

1、性能定义

要查找和评估器性能瓶颈,首先要知道性能定义,对于jvm调优来说,我们需要知道以下三个定义属性,依作为评估基础:

  • 吞吐量:重要指标之一,是指不考虑垃圾收集引起的停顿时间或内存消耗,垃圾收集器能支撑应用达到的最高性能指标。
  • 延迟:其度量标准是缩短由于垃圾啊收集引起的停顿时间或者完全消除因垃圾收集所引起的停顿,避免应用运行时发生抖动。
  • 内存占用:垃圾收集器流畅运行所需要 的内存数量。

这三个属性中,其中一个任何一个属性性能的提高,几乎都是以另外一个或者两个属性性能的损失作代价,不可兼得,具体某一个属性或者两个属性的性能对应用来说比较重要,要基于应用的业务需求来确定。

2、性能调优原则

在调优过程中,我们应该谨记以下3个原则,以便帮助我们更轻松的完成垃圾收集的调优,从而达到应用程序的性能要求。

1. MinorGC回收原则: 每次minor GC 都要尽可能多的收集垃圾对象。以减少应用程序发生Full GC的频率。

2. GC内存最大化原则:处理吞吐量和延迟问题时候,垃圾处理器能使用的内存越大,垃圾收集的效果越好,应用程序也会越来越流畅。

3. GC调优3选2原则: 在性能属性里面,吞吐量、延迟、内存占用,我们只能选择其中两个进行调优,不可三者兼得。

3、性能调优流程

以上就是对应用程序进行jvm调优的基本流程,我们可以看到,jvm调优是根据性能测试结果不断优化配置而多次迭代的过程。在达到每一个系统需求指标之前,之前的每个步骤都有可能经历多次迭代。有时候为了达到某一方面的指标,有可能需要对之前的参数进行多次调整,进而需要把之前的所有步骤重新测试一遍。

另外调优一般是从满足程序的内存使用需求开始的,之后是时间延迟的要求,最后才是吞吐量的要求,要基于这个步骤来不断优化,每一个步骤都是进行下一步的基础,不可逆行之。以下我们针对每个步骤进行详细的示例讲解。

在JVM的运行模式方面,我们直接选择server模式,这也是jdk1.6以后官方推荐的模式。

在垃圾收集器方面,我们直接采用了jdk1.6-1.8 中默认的parallel收集器(新生代采用parallelGC,老生代采用parallelOldGC)。

三、确定内存占用

在确定内存占用之前,我们需要知道两个知识点:

  1. 应用程序的运行阶段
  2. jvm内存分配

1、运行阶段

应用程序的运行阶段,我可以划分为以下三个阶段:

1、初始化阶段 : jvm加载应用程序,初始化应用程序的主要模块和数据。

2、稳定阶段:应用在此时运行了大多数时间,经历过压力测试的之后,各项性能参数呈稳定状态。核心函数被执行,已经被jit编译预热过。

3、总结阶段:最后的总结阶段,进行一些基准测试,生成响应的策报告。这个阶段我们可以不关注。

确定内存占用以及活跃数据的大小,我们应该是在程序的稳定阶段来进行确定,而不是在项目起初阶段来进行确定,如何确定,我们先看以下jvm的内存分配。

2、jvm内存分配&参数

jvm堆中主要的空间,就是以上新生代、老生代、永久代组成,整个堆大小=新生代大小 + 老生代大小 + 永久代大小。 具体的对象提升方式,这里不再过多介绍了,我们看下一些jvm命令参数,对堆大小的指定。如果不采用以下参数进行指定的话,虚拟机会自动选择合适的值,同时也会基于系统的开销自动调整。

在设置的时候,如果关注性能开销的话,应尽量把永久代的初始值与最大值设置为同一值,因为永久代的大小调整需要进行FullGC 才能实现。

3、计算活跃数据大小

计算活跃数据大小应该遵循以下流程:

如前所述,活跃数据应该是基于应用程序稳定阶段时,观察长期存活与对象在java堆中占用的空间大小。

计算活跃数据时应该确保以下条件发生:

1.测试时,启动参数采用jvm默认参数,不人为设置。

2.确保Full GC 发生时,应用程序正处于稳定阶段。

采用jvm默认参数启动,是为了观察应用程序在稳定阶段的所需要的内存使用。

如何才算稳定阶段?

一定得需要产生足够的压力,找到应用程序和生产环境高峰符合状态类似的负荷,在此之后达到峰值之后,保持一个稳定的状态,才算是一个稳定阶段。所以要达到稳定阶段,压力测试是必不可少的,具体如何如何对应用压力测试,本篇不过多说明,后期会有专门介绍的篇幅。

在确定了应用出于稳定阶段的时候,要注意观察应用的GC日志,特别是Full GC 日志。

GC日志指令: -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:<filename>

GC日志是收集调优所需信息的最好途径,即便是在生产环境,也可以开启GC日志来定位问题,开启GC日志对性能的影响极小,却可以提供丰富数据。

必须得有FullGC 日志,如果没有的话,可以采用监控工具强制调用一次,或者采用以下命令,亦可以触发

jmap -histo:live pid

在稳定阶段触发了FullGC我们一般会拿到如下信息:

从以上gc日志中,我们大概可以分析到,在发生fullGC之时,整个应用的堆占用以及GC时间,当然了,为了更加精确,应该多收集几次,获取一个平均值。或者是采用耗时最长的一次FullGC来进行估算。

在上图中,fullGC之后,老年代空间占用在93168kb(约93MB),我们以此定为老年代空间的活跃数据。

其他堆空间的分配,基于以下规则来进行

基于以上规则和上图中的FullGC信息,我们现在可以规划的该应用堆空间为:

java 堆空间: 373Mb (=老年代空间93168kb*4)

新生代空间:140Mb(=老年代空间93168kb*1.5)

永久代空间:5Mb(=永久代空间3135kb*1.5)

老年代空间: 233Mb=堆空间-新生代看空间=373Mb-140Mb

对应的应用启动参数应该为:

java -Xms373m -Xmx373m -Xmn140m -XX:PermSize=5m -XX:MaxPermSize=5m

四、延迟调优

在确定了应用程序的活跃数据大小之后,我们需要再进行延迟性调优,因为对于此时堆内存大小,延迟性需求无法达到应用的需要,需要基于应用的情况来进行调试。

在这一步进行期间,我们可能会再次优化堆大小的配置,评估GC的持续时间和频率、以及是否需要切换到不同的垃圾收集器上。

1、系统延迟需求

在调优之前,我们需要知道系统的延迟需求是那些,以及对应的延迟可调优指标是那些。

  • 应用程序可接受的平均停滞时间: 此时间与测量的Minor GC持续时间进行比较。
  • 可接受的Minor GC频率:Minor GC的频率与可容忍的值进行比较。
  • 可接受的最大停顿时间: 最大停顿时间与最差情况下FullGC的持续时间进行比较。
  • 可接受的最大停顿发生的频率:基本就是FullGC的频率。

以上中,平均停滞时间和最大停顿时间,对用户体验最为重要,可以多关注。

基于以上的要求,我们需要统计以下数据:

  • MinorGC的持续时间;
  • 统计MinorGC的次数;
  • FullGC的最差持续时间;
  • 最差情况下,FullGC的频率;

2、优化新生代的大小

比如如上的gc日志中,我们可以看到Minor GC的平均持续时间=0.069秒,MinorGC 的频率为0.389秒一次。

如果,我们系统的设置的平均停滞时间为50ms,当前的69ms明显是太长了,就需要调整。

我们知道新生代空间越大,Minor GC的GC时间越长,频率越低。

如果想减少其持续时长,就需要减少其空间大小。

如果想减小其频率,就需要加大其空间大小。

为了降低改变新生代的大小对其他区域的最小影响。在改变新生代空间大小的时候,尽量保持老年代空间的大小。

比如此次减少了新生代空间10%的大小,应该保持老年代和持代的大小不变化,第一步调优后的参数如下变化:

java -Xms359m -Xmx359m -Xmn126m -XX:PermSize=5m -XX:MaxPermSize=5m

新生代的大小有140m变为126,堆大小顺应变化,此时老年代是没有变化的。

3、优化老年代的大小

同上一步一样,在优化之前,也需要采集gc日志的数据。此次我们关注的是FullGC的持续时间和频率。

上图中,我们可以看到

FullGC 平均频率 =5.8s

FullGC 平均持续时间=0.14s

(以上为了测试,真实项目的fullGC 没有这么快)

如果没有FullGC的日志,有办法可以评估么?

我们可以通过对象提升率进行计算。

对象提升率

比如上述中启动参数中,我们的老年代大小=233Mb。

那么需要多久才能填满老年代中这233Mb的空闲空间取决于新生代到老年代的提升率。

每次提升老年代占用量=每次MinorGC 之后 java堆占用情况 减去 MinorGC后新生代的空间占用

对象提升率=平均值(每次提升老年代占用量) 除以 老年代空间

有了对象提升率,我们就可以算出填充满老年代空间需要多少次minorGC,大概一次fullGC的时间就可以计算出来了。

比如:

上图中:

第一次minor GC 之后,老年代空间:13740kb - 13732kb =8kb

第二次minor GC 之后,老年代空间:22394kb - 17905kb =4489kb

第三次minor GC 之后,老年代空间:34739kb - 17917kb =16822kb

第四次minor GC 之后,老年代空间:48143kb - 17913kb =30230kb

第五次minor GC 之后,老年代空间:62112kb - 17917kb =44195kb

老年代每次minorGC提升率

4481kb 第二次和第一次minorGC之间

12333kb 第3次和第2次minorGC之间

13408kb 第4次和第3次minorGC之间

13965kb 第5次和第4次minorGC之间

我们可以测算出:

每次minorGC 的平均提升为12211kb,约为12Mb

上图中,平均minorGC的频率为 213ms/次

提升率=12211kb/213ms=57kb/ms

老年代空间233Mb ,占满大概需要233*1024/57=4185ms 约为4.185s。

FullGC的预期最差频率时长可以通过以上两种方式估算出来,可以调整老年代的大小来调整FullGC的频率,当然了,如果FullGC持续时间过长,无法达到应用程序的最差延迟要求,就需要切换垃圾处理器了。具体如何切换,下篇再讲,比如切换为CMS,针对CMS的调优方式又有会细微的差别。

五、吞吐量调优

经过上述漫长 调优过程,最终来到了调优的最后一步,这一步对上述的结果进行吞吐量测试,并进行微调。

吞吐量调优主要是基于应用程序的吞吐量要求而来的,应用程序应该有一个综合的吞吐指标,这个指标基于真个应用的需求和测试而衍生出来的。当有应用程序的吞吐量达到或者超过预期的吞吐目标,整个调优过程就可以圆满结束了。

如果出现调优后依然无法达到应用程序的吞吐目标,需要重新回顾吞吐要求,评估当前吞吐量和目标差距是否巨大,如果在20%左右,可以修改参数,加大内存,再次从头调试,如果巨大就需要从整个应用层面来考虑,设计以及目标是否一致了,重新评估吞吐目标。

对于垃圾收集器来说,提升吞吐量的性能调优的目标就是就是尽可能避免或者很少发生FullGC 或者Stop-The-World压缩式垃圾收集(CMS),因为这两种方式都会造成应用程序吞吐降低。尽量在MinorGC 阶段回收更多的对象,避免对象提升过快到老年代。

六、最后

据Plumbr公司对特定垃圾收集器使用情况进行了一次调查研究,研究数据使用了84936个案例。在明确指定垃圾收集器的13%的案例中,并发收集器(CMS)使用次数最多;但大多数案例没有选择最佳垃圾收集器。这个比例占用在87%左右。

JVM调优是一个系统而又复杂的工作,目前jvm下的自动调整已经做的比较优秀,基本的一些初始参数都可以保证一般的应用跑的比较稳定了,对部分团队来说,程序性能可能优先级不高,默认垃圾收集器已经够用了。调优要基于自己的情况而来。



本文作者:wier_ali

阅读原文

本文为云栖社区原创内容,未经允许不得转载。

查看原文

赞 31 收藏 27 评论 0

你若安好便是晴天 发布了文章 · 8月5日

使用springcloud alibaba搭建的微服务系统

1.git仓库地址

https://gitee.com/QingXinDeChengXuYuan/xiayuedu.git

2.说明

  • 注册中心及配置中心使用nacos
  • 网关使用springcloud gateway
  • 服务调用使用 openfeign
  • xiayu-admin-gateway

    • 后台管理网关
    • 主要负责后台接口,管理员token生成等等
    • 逻辑同公共网关
  • xiayu-common

    • 公用模块:主要是工具类,配置类等等
    • redis采用哨兵模式,并且创建缓存管理器,redisson客户端
    • kafka 消息确认方式使用all,消息幂等性通过数据库进行确认
    • 数据库使用一主一从
    • 国际化,自动注入MessageSource即可
    • 权限控制使用spring security,权限信息存储在token,基于角色信息控制权限
    • 分布式id使用雪花算法
    • 每个服务的nodeId,使用zookeeper的临时顺序节点
    • 可根据配置文件项,设置开启与关闭
    • 每次请求的traceId通过当前线程局部变量+1
    • 封装CommonResponse、CommonRequest和异常
    • 加密工具类
  • xiayu-integration

    • 发送短信,邮件服务 //todo
  • xiayu-job

    • 定时任务服务
    • 定义了定时任务模版,每个定时任务需要分布式锁
  • xiayu-public-gateway

    • 用户网关服务
    • 网关服务主要对token进行签发,签发之后会保存到redis中,签发是通过注册或登录接口返回response的值进行判断的
    • token验证,首先查看redis中是否存在
    • 每次请求,验证token并更新过期时间
  • xiayu-user

    • 用户服务
    • 用户注册登录,角色权限
  • 按照需求可以额外添加服务
查看原文

赞 1 收藏 1 评论 0

你若安好便是晴天 发布了文章 · 8月4日

分析java程序cpu占用较高原因过程

1.Java程序

import java.io.Serializable;

public class Test {
    public static void main(String[] args) throws InterruptedException {
        while (true){ //一直循环创建User对象
            User user = new User();
            user.userName1 = "username1";
            user.userName2 = "userName2";
            user.userName3 = "userName3";
            user.userName4 = "userName4";
            user.userName5 = "userName5";
            user.userName6 = "userName6";
            user.userName7 = "userName7";
            user.userName8 = "userName8";
        }

    }

    public static void service(){
        return;
    }

    public static class User implements Serializable {
        public String userName1;
        public String userName2;
        public String userName3;
        public String userName4;
        public String userName5;
        public String userName6;
        public String userName7;
        public String userName8;
        public String userName9;
        public String userName10;
        public String userName11;
        public String userName12;
        public String userName13;
        public String userName14;
        public String userName15;
        public String userName16;
    }
}

2.top命令查看cpu占用率

top - 09:56:17 up 1 day,  9:53,  7 users,  load average: 1.34, 0.95, 0.77
Tasks: 139 total,   2 running, 137 sleeping,   0 stopped,   0 zombie
%Cpu(s): 51.9 us,  3.8 sy,  0.0 ni, 43.6 id,  0.0 wa,  0.0 hi,  0.7 si,  0.0 st
KiB Mem :  3880320 total,   137000 free,  3496672 used,   246648 buff/cache
KiB Swap:  2097148 total,  2076660 free,    20488 used.   159388 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                                                     
17679 root      20   0 3665040  32140  15900 S  91.0  0.8   0:18.84 java                                                                        
15862 root      20   0  155032   5624   3936 S   0.7  0.1   0:30.12 sshd                                                                        
24986 root      20   0 4607680 998.2m  12640 S   0.7 26.3  18:24.92 java                                                                        
23783 root      20   0  151708   6408   1112 S   0.3  0.2   6:36.31 redis-sentinel                                                              
23790 root      20   0  149448   4840   1108 R   0.3  0.1   6:39.38 redis-sentinel                                                              
25350 root      20   0 4567896 973220  11788 S   0.3 25.1  12:45.53 java                                                                        
25716 root      20   0 4574928 994456  11788 S   0.3 25.6  12:47.27 java                                                                        
28449 root      20   0  155160   5644   3936 S   0.3  0.1   0:22.72 sshd                                                                        
28610 root      20   0  162832   3032   1572 S   0.3  0.1   0:11.98 top                                                                         
30011 root      20   0  155148   5628   3936 S   0.3  0.1   0:13.78 sshd                                                                        
30158 root      20   0  162832   3024   1556 S   0.3  0.1   0:06.20 top                                                                         
    1 root      20   0  128104   4568   2736 S   0.0  0.1   0:04.42 systemd                                                                     
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.04 kthreadd                                                                    
    4 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H                                                                
    6 root      20   0       0      0      0 S   0.0  0.0   0:09.35 ksoftirqd/0

3.查看进程对应的线程占用cpu

top -p 23191 -H

PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                                                      
23192 root      20   0 3665040  30560  15772 R 91.3  0.8   2:06.06 java                                                                         
23191 root      20   0 3665040  30560  15772 S  0.0  0.8   0:00.00 java                                                                         
23193 root      20   0 3665040  30560  15772 S  0.0  0.8   0:00.01 GC Thread#0                                                                  
23194 root      20   0 3665040  30560  15772 S  0.0  0.8   0:00.00 G1 Main Marker                                                               
23195 root      20   0 3665040  30560  15772 S  0.0  0.8   0:00.00 G1 Conc#0                                                                    
23196 root      20   0 3665040  30560  15772 S  0.0  0.8   0:00.00 G1 Refine#0                                                                  
23197 root      20   0 3665040  30560  15772 S  0.0  0.8   0:00.01 G1 Young RemSet

4.获取23191线程堆栈信息

jstack 23191 > 23191.log,找到对应的高cpu占用线程23192(0x5d54)线程的状态。可以通过堆栈信息看到是Test.java中的问题

"main" #1 prio=5 os_prio=0 cpu=265491.21ms elapsed=290.57s tid=0x00007f5830014800 nid=0x5d54 runnable  [0x00007f58384a3000]
java.lang.Thread.State: RUNNABLE
     at Test.main(Test.java:15)

5.上述可以通过top + jstack进行查找高cpu线程和其堆栈信息

也可以通过脚本来实现查询高cpu占用线程

wget --no-check-certificate https://github.com/oldratlee/useful-scripts/archive/release-2.x.zip

unzip release-2.x.zip

github上的开源脚本
直接执行
/data/java/useful-scripts-release-2.x/bin中的

show-busy-java-threads脚本即可查看,默认显示前5名线程

结果

[1] Busy(90.4%) thread(14366/0x381e) stack of java process(14365) under user(root):
"main" #1 prio=5 os_prio=0 cpu=16787.88ms elapsed=18.53s tid=0x00007f4964014800 nid=0x381e runnable  [0x00007f496dc9f000]
   java.lang.Thread.State: RUNNABLE
        at Test.main(Test.java:15)

[2] Busy(0.0%) thread(14365/0x381d) stack of java process(14365) under user(root):

[3] Busy(0.0%) thread(24040/0x5de8) stack of java process(24040) under user(root):

[4] Busy(0.0%) thread(24043/0x5deb) stack of java process(24040) under user(root):
"main" #1 prio=5 os_prio=0 cpu=787.79ms elapsed=121813.53s tid=0x00007f9e9c015000 nid=0x5deb in Object.wait()  [0x00007f9ea6296000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(java.base@11.0.7/Native Method)
        - waiting on <0x00000000c631f8a0> (a org.apache.zookeeper.server.quorum.QuorumPeer)
        at java.lang.Thread.join(java.base@11.0.7/Thread.java:1305)
        - waiting to re-lock in wait() <0x00000000c631f8a0> (a org.apache.zookeeper.server.quorum.QuorumPeer)
        at java.lang.Thread.join(java.base@11.0.7/Thread.java:1379)
        at org.apache.zookeeper.server.quorum.QuorumPeerMain.runFromConfig(QuorumPeerMain.java:171)
        at org.apache.zookeeper.server.quorum.QuorumPeerMain.initializeAndRun(QuorumPeerMain.java:114)
        at org.apache.zookeeper.server.quorum.QuorumPeerMain.main(QuorumPeerMain.java:81)

[5] Busy(0.0%) thread(24098/0x5e22) stack of java process(24098) under user(root):
查看原文

赞 1 收藏 1 评论 0

你若安好便是晴天 发布了文章 · 7月21日

查看kafka基本信息命令

将miner-profit topic副本由一个增加到3个

replication.json

{
    "version": 1, 
    "partitions": [
        {
            "topic": "miner-profit", 
            "partition": 0, 
            "replicas": [
                1, 
                2, 
                3
            ]
        },
        {
            "topic": "miner-profit", 
            "partition": 1, 
            "replicas": [
                1, 
                2, 
                3
            ]
        },
        {
            "topic": "miner-profit",
            "partition": 2,
            "replicas": [
                1,
                2,
                3
            ]
        }
    ]
}

执行命令

../kafka1/bin/kafka-reassign-partitions.sh --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183 --reassignment-json-file replication.json --execute

查看所有topic

./kafka1/bin/kafka-topics.sh --list --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183

创建topic

./kafka1/bin/kafka-topics.sh --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183 --create --replication-factor 2 --partitions 2 --topic user-login

删除topic

./kafka1/bin/kafka-topics.sh --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183 --delete --topic user-regisger

增加topic分区数目

./kafka1/bin/kafka-topics.sh --alter  --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183  --topic miner-profit  --partitions 3 

查看topic分区信息

./kafka1/bin/kafka-topics.sh --describe --zookeeper dev1.xiayu.com:2181,dev2.xiayu.com:2182,dev3.xiayu.com:2183 --topic miner-profit

查看一个topic中的所有消息

./kafka1/bin/kafka-console-consumer.sh --bootstrap-server dev1.xiayu.com:9092,dev2.xiayu.com:9093,dev3.xiayu.com:9094 --topic miner-profit --from-beginning

查看所有消费者组

./kafka1/bin/kafka-consumer-groups.sh --list --bootstrap-server dev1.xiayu.com:9092,dev2.xiayu.com:9093,dev3.xiayu.com:9094

查看消费者组消费情况

./kafka1/bin/kafka-consumer-groups.sh --bootstrap-server dev1.xiayu.com:9092,dev2.xiayu.com:9093,dev3.xiayu.com:9094 --group xiayu-consumer-group --describe

cmak 启动

./cmak-3.0.0.5/bin/cmak -Dconfig.file=./cmak-3.0.0.5/conf/application.conf -Dhttp.port=8333
查看原文

赞 0 收藏 0 评论 0

你若安好便是晴天 发布了文章 · 7月12日

redis哨兵模式从节点的哨兵之间不能通信

1.问题

从节点中自动生成的配置文件中,发现从节点的哨兵只能和主节点的哨兵进行通信,和从节点的哨兵不能通信
image.png

image.png

2.解决方法

在从节点的哨兵配置文件中
sentinels monitor 监视的不是从节点的端口,也应该是主节点的端口

sentinel monitor xiayu_master 192.168.1.108 6379 2
查看原文

赞 0 收藏 0 评论 0

你若安好便是晴天 发布了文章 · 7月5日

非法参数错误 a header value must not end with '\r' or '\n':

1.问题

在将生成的token放入头信息中,报如下错误

originalResponse.getHeaders().add(JWTConstant.H5_CLIENT_TOKEN, token);

错误

java.lang.IllegalArgumentException: a header value must not end with '\r' or '\n':eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJLUWo1UGthQS9FZU4vUnQzTEFvQXJOTHhLTFBrY0lzeklPNFV5cnlKaGtaOGg2YkVRaEFNeGIzU0YxaUc2S1pPIiwiaXNzIjoieGlheXUiLCJzdWIiOiJ4aWF5dS11c2VyIiwiYXVkIjoieGlheXUtdXNlciIsImlhdCI6Ij\r\nIwMjAwNzA1VDAwMzEwMS43MTYrMDgwMCIsIm5iZiI6IjIwMjAwNzA1VDAw\r\nMzEwMS43MTYrMDgwMCIsImV4cCI6IjIwMjAwNzA1VDAwMzEwNS4zMTYrMDgwMCJ9.a91ea52fea38bdd343bde3488ed35d9c0ad8e522c22d264b3d88c287d144aff4

\r\n是我添加上去的

token在生成过程中,使用了Base64进行编码,java原生的base64编码按照规范会在64个字符后自动加rn,就导致了这个问题。

解决方法:
a. 将生成后包含的rn字符串去掉
b. 换成Apache commons 家族中的common codec工具进行base64编解码,我采用的是这种方案。

查看原文

赞 0 收藏 0 评论 0

你若安好便是晴天 发布了文章 · 6月30日

READONLY You can't write against a read only replica错误

1.错误

2020-06-29 22:54:18.610  INFO 12176 --- [           main] com.xiayu.test.RedisConfigurationTest    : Started RedisConfigurationTest in 2.011 seconds (JVM running for 3.247)

org.springframework.dao.InvalidDataAccessApiUsageException: READONLY You can't write against a read only replica.; nested exception is redis.clients.jedis.exceptions.JedisDataException: READONLY You can't write against a read only replica.

    at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:69)
    at org.springframework.data.redis.connection.jedis.JedisExceptionConverter.convert(JedisExceptionConverter.java:42)
    at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
    at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
    at org.springframework.data.redis.connection.jedis.JedisConnection.convertJedisAccessException(JedisConnection.java:135)
    at org.springframework.data.redis.connection.jedis.JedisStringCommands.convertJedisAccessException(JedisStringCommands.java:751)
    at org.springframework.data.redis.connection.jedis.JedisStringCommands.set(JedisStringCommands.java:144)
    at org.springframework.data.redis.connection.DefaultedRedisConnection.set(DefaultedRedisConnection.java:281)
    at org.springframework.data.redis.core.DefaultValueOperations$3.inRedis(DefaultValueOperations.java:240)
    at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:60)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188)
    at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96)
    at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:236)
    at com.xiayu.test.RedisConfigurationTest.redisTemplate(RedisConfigurationTest.java:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: redis.clients.jedis.exceptions.JedisDataException: READONLY You can't write against a read only replica.
    at redis.clients.jedis.Protocol.processError(Protocol.java:132)
    at redis.clients.jedis.Protocol.process(Protocol.java:166)
    at redis.clients.jedis.Protocol.read(Protocol.java:220)
    at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:318)
    at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:236)
    at redis.clients.jedis.BinaryJedis.set(BinaryJedis.java:216)
    at org.springframework.data.redis.connection.jedis.JedisStringCommands.set(JedisStringCommands.java:142)
    ... 38 more


Process finished with exit code -1

2.问题

redis的哨兵配置有问题,将myid删除,重新启动,就好了

2020-06-30 00:23:46.987  INFO 17812 --- [           main] com.xiayu.test.RedisConfigurationTest    : Starting RedisConfigurationTest on DESKTOP-4O3UT4O with PID 17812 (started by xiyue in C:\Users\xiyue\xiayuedu\xiayu-common)
2020-06-30 00:23:46.988  INFO 17812 --- [           main] com.xiayu.test.RedisConfigurationTest    : No active profile set, falling back to default profiles: default
2020-06-30 00:23:47.923  INFO 17812 --- [           main] c.x.c.config.redis.RedisConfiguration    : sentinel:[192.168.1.104:26379, 192.168.1.104:26380, 192.168.1.104:26381],masterName:xiayu_master
2020-06-30 00:23:48.101  INFO 17812 --- [           main] redis.clients.jedis.JedisSentinelPool    : Trying to find master from available Sentinels...
2020-06-30 00:23:48.158  INFO 17812 --- [           main] redis.clients.jedis.JedisSentinelPool    : Redis master running at 192.168.1.104:6379, starting Sentinel listeners...
2020-06-30 00:23:48.317  INFO 17812 --- [           main] redis.clients.jedis.JedisSentinelPool    : Created JedisPool to master at 192.168.1.104:6379
查看原文

赞 0 收藏 0 评论 0

你若安好便是晴天 赞了文章 · 6月15日

SpringCloud Alibaba 微服务实战六 - 配置隔离

导读:本篇作为SpringCloud Alibaba微服务实战系列的第六篇,主要内容是将所有的微服务接入Nacos配置中心并按环境进行隔离。系列文章,欢迎持续关注。

配置中心集成

在SpringCloud Alibaba体系中是使用Nacos作为配置中心,这样Nacos既可以作为注册中心也可以作为配置中心,相当于SpringCloud中的Eureka + Config。要接入Nacos的配置中心也很简单,只需要如下几步:

  • 引入配置中心组件spring-cloud-starter-alibaba-nacos-config
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
  • 在resource中新建文件bootstrap.yml,配置应用名称和注册中心地址
spring:
  application:
    name: account-service
  cloud:
    nacos:
      config:
        server-addr: 10.0.10.48:8848
        file-extension: yml

这里指定文件后缀为yml

  • 在nacos控制台建立配置文件

注:在客户端和server端对文件格式的声明要一致,例如在客户端声明的是yml,则server端添加配置文应该是${spring.application.name}.yml,若写成${spring.application.name}.yaml,则会找不到配置文件。
如在我们的account-service中应该建立一个account-service.yml文件,然后把原application.yml的内容复制到里面即可,在nacos配置中可以删除spring.application.name 配置。
image.png

  • 运行测试

测试服务功能是否正常

简单几步就可以将你的微服务集成Nacos的配置中心了,但是在实际开发中我们需要准备多个环境的配置文件(开发、测试、生产),接下来我们看看如何使用Nacos进行多环境配置管理。

多环境管理

在Nacos中实现多环境管理很容易,可以通过Profiles, Group, Namespace三个维度实现。

profiles

nacos配置中心的DATA ID就相当于一个配置文件的名称,前面一部分我们DATA ID的默认命名方式为`${spring.application.name}.${
spring.cloud.nacos.config.file-extension},比如我们之前的配置文件为account-service.yml`。

如果我们在服务启动的时候指定了服务的环境名称spring.profiles.active,那么DATA ID的命名方式就变成了${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}。比如上面的account-service我们在bootstrap.yml中设置了spring.profiles.active=DEV,那么服务启动时就会去nacos抓取account-service-DEV.yml这个配置文件的数据。

利用Nacos命名方式的特性我们可以实现多环境配置管理。

实战

  • 给系统指定环境名称
spring:
  profiles:
    active: DEV
  • Nacos中建立多套环境的配置

image.png

  • 启动应用,观察后端启动日志
2019-12-13 13:39:21,352 INFO  NacosPropertySourceBuilder:87 - Loading nacos data, dataId: 'account-service-DEV.yml', group: 'DEFAULT_GROUP'
2019-12-13 13:39:21,363 INFO  PropertySourceBootstrapConfiguration:101 - Located property source: CompositePropertySource {name='NACOS', propertySources=[NacosPropertySource {name='account-service-DEV.yml'}, NacosPropertySource {name='account-service.yml'}]}
2019-12-13 13:39:21,376 INFO  AccountServiceApplication:652 - The following profiles are active: DEV

Group

Group是DATA ID集合的概念,将同一个环境下的配置文件组成一个Group,在服务启动时通过指定spring.cloud.nacos.config.group属性可以让系统加载不同Group的配置文件。

实战

  • 在nacos中建立多个配置文件,以Group进行区分

image.png
如上所示,我们建立了一个DEV_GROUP 和TEST_GROUP。

  • 修改配置文件,指定spring.cloud.nacos.config.group的值
spring:
  cloud:
    nacos:
      config:
        server-addr: 10.0.10.48:8848
        file-extension: yml
        group: DEV_GROUP

这个时候不需要再给服务指定spring.profiles.active属性

  • 启动应用,观察后端启动日志
2019-12-13 14:19:19,861 INFO  NacosPropertySourceBuilder:87 - Loading nacos data, dataId: 'account-service.yml', group: 'DEV_GROUP'
2019-12-13 14:19:19,869 INFO  PropertySourceBootstrapConfiguration:101 - Located property source: CompositePropertySource {name='NACOS', propertySources=[NacosPropertySource {name='account-service.yml'}]}
2019-12-13 14:19:19,882 INFO  AccountServiceApplication:648 - No active profile set, falling back to default profiles: default

Namespace

Namespace 是 nacos 做多环境以及多租户数据(配置和服务)隔离的,目前也是官方推荐的多环境支持方案。如果你的微服务有多套不同的环境(DEV,TEST),那么这个时候可以根据指定的环境来创建不同的 namespce,以此来实现多环境的隔离。不同的命名空间下可以有相同的GroupData ID

实战

  • Nacos中根据项目环境建立多套Namespace

image.png
建好Namespace后会生产一串命名空间ID,在应用里通指定spring.cloud.nacos.config.namespace属性对应不同的Namespace

  • 在不同的命名空间下分别建立配置文件account-service.yml

image.png

  • 修改配置文件,指定spring.cloud.nacos.config.namespace属性
spring:
  cloud:
    nacos:
      config:
        server-addr: 10.0.10.48:8848
        file-extension: yml
        namespace: 7e8ccc22-6f51-42fa-bcf1-db45f99dbf57
  • 启动应用,观察后端启动日志

image.png
这个版本没有关于Namespace的启动日志,不过你可以修改你的配置确认是否正常加载,反正我这里是正常的O(∩_∩)O哈哈~

Namespace作为官方推荐的环境隔离配置,我们将所有的微服务全部按照此方式进行改造,改造完的效果如下:
image.png

改造完成后要对服务进行测试,保证服务能正常运行。

至此我们已经把我们微服务的配置全部放入了配置中心,并且使用Namespace对环境进行了隔离。那么本期的“SpringCloud Alibaba微服务实战六 - 配置隔离”篇也就该结束啦,咱们下期有缘再见!
image.png

再见之前让我在求一波关注吧,O(∩_∩)O哈哈~!
image.png

系列文章
欢迎扫码关注微信公众号或 个人博客
查看原文

赞 1 收藏 0 评论 0

认证与成就

  • 获得 12 次点赞
  • 获得 1 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 1 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2019-12-25
个人主页被 389 人浏览