• [技术干货] 关于杀死Chrome进程后的“恢复页面弹窗”的永久关闭
    相信大家都有遇见,每当我们杀死Chrome进程后,再次打开Chrome,就会在右上角有一个恢复页面的弹窗,很烦人,有时候还会影响到流程的进行。不用担心,接下来就告诉大家如何永久关闭这个“恢复”弹窗。1.首先我们找到自己磁盘中的appdata目录(一般这个都在C盘User目录下,觉得不太好找的可以试试在路径上直接输入%appdata%,回车,再返回这个目录的上一级就可以了)2.然后我们找到后面的地址  C:\Users\+\AppData\Local\Google\Chrome\User Data\Default 在这个目录下找到Preferences文件3.我们需要右键选择用记事本打开Preferences文件,在里面直接快捷键CTRL+F 去查找exit_type,将"exit_type":"crashed" 改为"Normal"4.然后我们只需要保存关闭Preferences文件,再右键打开Preferences文件属性,将只读这个选项勾上,保存。即可享受永不弹出“恢复页面”的Chrome浏览器了!
  • [新手课堂] 垃圾收集器 Garbage Collector
    Serial 收集器:一个采用单个线程并基于复制算法工作在新生代的收集器,进行垃圾收集时,必须暂停其它所有的工作线程(Stop The World),是 Client 模式下 JVM 的默认选项,-XX:+UseSerialGCSerial Old 收集器:一个采用单线程基于标记-整理算法并工作在老年代的收集器ParNew 收集器:Serial 收集器的多线程版本(使用多个线程进行垃圾收集),-XX:+UseParNewGCCMS 收集器(Concurrent Mark Sweep):一种以尽量减少停顿时间为目标的收集器,工作在老年代,基于标记-清除算法实现,-XX:+UseConcMarkSweepGCParallel Scavenge 收集器:一个采用多线程基于复制算法并工作在新生代的收集器,也被称作是吞吐量优先的 GC,是早期 jdk1.8 等版本中 Server 模式 JVM 的默认 GC 选择,-XX:+UseParallelGC,使用 Parallel Scavenge(年轻代)+ Serial Old(老年代)的组合进行 GCParallel Old 收集器:一个采用多线程基于标记-整理算法并工作在老年代的收集器,适用于注重于吞吐量及 CPU 资源敏感的场合,-XX:+UseParallelOldGC,使用 Parallel Scavenge(年轻代)+ Parallel Old(老年代)的组合进行 GCG1 收集器:jdk1.7 提供的一个工作在新生代和老年代的收集器,基于标记-整理算法实现,在收集结束后可以避免内存碎片问题,一种兼顾吞吐量和停顿时间的 GC,是 Oracle jdk1.9 以后的默认 GC 选项
  • [新手课堂] IO底层工作原理
    1.3.1 缓存处理和内核vs用户空间缓冲与缓冲的处理方式,是所有I/O操作的基础。术语输入、输出只对数据移入和移出缓存有意义。任何时候都要把它记在心中。通常,进程执行操作系统的I/O请求包括数据从缓冲区排出(写操作)和数据填充缓冲区(读操作)。这就是I/O的整体概念。在操作系统内部执行这些传输操作的机制可以非常复杂,但从概念上讲非常简单。上图显示了一个简化的逻辑图,它表示块数据如何从外部源,例如一个磁盘,移动到进程的存储区域(例如RAM)中。首先,进程要求其缓冲通过read()系统调用填满。这个系统调用导致内核向磁盘控制硬件发出一条命令要从磁盘获取数据。磁盘控制器通过DMA直接将数据写入内核的内存缓冲区,不需要主CPU进一步帮助。当请求read()操作时,一旦磁盘控制器完成了缓存的填写,内核从内核空间的临时缓存拷贝数据到进程指定的缓存中。有一点需要注意,在内核试图缓存及预取数据时,内核空间中进程请求的数据可能已经就绪了。如果这样,进程请求的数据会被拷贝出来。如果数据不可用,则进程被挂起。内核将把数据读入内存。1.3.2 虚拟内存所有现代操作系统都使用虚拟内存。虚拟内存意味着人工或者虚拟地址代替物理(硬件RAM)内存地址。虚拟地址有两个重要优势:多个虚拟地址可以映射到相同的物理地址一个虚拟地址空间可以大于实际可用硬件内存在上面介绍中,从内核空间拷贝到最终用户缓存看起来增加了额外的工作。为什么不告诉磁盘控制器直接发送数据到用户空间的缓存呢?好吧,这是由虚拟内存实现的。用到了上面的优势1。通过将内核空间地址映射到相同的物理地址作为一个用户空间的虚拟地址,DMA硬件(只能访问物理内存地址)可以填充缓存。这个缓存同时对内核和用户空间进程可见。这就消除了内核和用户空间之间的拷贝,但是需要内核和用户缓冲区使用相同的页面对齐方式。缓冲区必须使用的块大小的倍数磁盘控制器(通常是512字节的磁盘扇区)。操作系统将其内存地址空间划分为页面,这是固定大小的字节组。这些内存页总是磁盘块大小的倍数和通常为2倍(简化寻址)。典型的内存页面大小是1024、2048和4096字节。虚拟和物理内存页面大小总是相同的。1.3.3 内存分页为了支持虚拟内存的第2个优势(拥有大于物理内存的可寻址空间)需要进行虚拟内存分页(通常称为页交换)。这种机制凭借虚拟内存空间的页可以持久保存在外部磁盘存储,从而为其他虚拟页放入物理内存提供了空间。本质上讲,物理内存担当了分页区域的缓存。分页区是磁盘上的空间,内存页的内容被强迫交换出物理内存时会保存到这里。调整内存页面大小为磁盘块大小的倍数,让内核可以直接发送指令到磁盘控制器硬件,将内存页写到磁盘或者在需要时重新加载。事实证明,所有的磁盘I/O操作都是在页面级别上完成的。这是数据在现代分页操作系统上在磁盘与物理内存之间移动的唯一方式。现代CPU包含一个名为内存管理单元(MMU)的子系统。这个设备逻辑上位于CPU与物理内存之间。它包含从虚拟地址向物理内存地址转化的映射信息。当CPU引用一个内存位置时,MMU决定哪些页需要驻留(通常通过移位或屏蔽地址的某些位)以及转化虚拟页号到物理页号(由硬件实现,速度奇快)。1.3.4 面向文件、块I/O文件I/O总是发生在文件系统的上下文切换中。文件系统跟磁盘是完全不同的事物。磁盘按段存储数据,每段512字节 。 它是硬件设备,对保存的文件语义一无所知。它们只是提供了一定数量的可以保存数据的插槽。从这方面来说,一个磁盘的段与内存分页类似。它们都有统一的大小并且是个可寻址的大数组另一方面,文件系统是更高层抽象。文件系统是 安排和翻译保存磁盘(或其它可随机访问,面向块的设备)数据的一种特殊方法。你写的代码几乎总是与文件系统交互,而不与磁盘直接交互。文件系统定义了文件名、路径、文件、文件属性等抽象。一个文件系统(在硬盘中)组织了一系列均匀大小的数据块。有些块保存元信息,如空闲块的映射、目录、索引等。其它块包含实际的文件数据。单个文件的元信息描述哪些块包含文件数据、数据结束位置、最后更新时间等。当用户进程发送请求来读取文件数据时,文件系统实现准确定位数据在磁盘上的位置。然后采取行动将这些磁盘扇区放入内存中。文件系统也有页的概念,它的大小可能与一个基本内存页面大小相同或者是它的倍数。典型的文件系统页面大小范围从2048到8192字节,并且总是一个基本内存页面大小的倍数。分页文件系统执行I/O可以归结为以下逻辑步骤:确定请求跨越了哪些文件系统分页(磁盘段的集合)。磁盘上的文件内容及元数据可能分布在多个文件系统页面上,这些页面可能是不连续的。分配足够多的内核空间内存页面来保存相同的文件系统页面。建立这些内存分页与磁盘上文件系统分页的映射。对每一个内存分页产生分页错误。虚拟内存系统陷入分页错误并且调度pagins(页面调入),通过从磁盘读取内容来验证这些页面。一旦pageins完成,文件系统分解原始数据来提取请求的文件内容或属性信息。需要注意的是,这个文件系统数据将像其它内存页一样被缓存起来。在随后的I/O请求中,一些数据或所有文件数据仍然保存在物理内存中,可以直接重用不需要从磁盘重读。1.3.5 文件锁定文件加锁是一种机制,一个进程可以阻止其它进程访问一个文件或限制其它进程访问该文件。虽然名为文件锁定,意味着锁定整个文件(经常做的)。锁定通常可以在一个更细粒度的水平。随着粒度下降到字节级,文件的区域通常会被锁定。锁与特定文件相关联,起始于文件的指定字节位置并运行到指定的字节范围。这一点很重要,因为它允许多个进程协作访问文件的特定区域而不妨碍别的进程在文件其它位置操作。文件锁有两种形式:共享和独占多个共享锁可以同时在相同的文件区域有效。独占锁要求没有其它锁对请求的区域有效。1.3.6 流I/O并非所有的I/O是面向块的。还有流I/O,它是管道的原型,必须顺序访问I/O数据流的字节。常见的数据流有TTY(控制台)设备、打印端口和网络连接。数据流通常但不一定比块设备慢,提供间歇性输入。大多数操作系统允许在非阻塞模式下工作。允许一个进程检查数据流的输入是否可用,不必在不可用时发生阻塞。这种管理允许进程在输入到达时进行处理,在输入流空闲时可以执行其他功能。比非阻塞模式更进一步的是有条件的选择(readiness selection)。它类似于非阻塞模式(并且通常建立在非阻塞模式基础上),但是减轻了操作系统检查流是否就绪准备的负担。操作系统可以被告知观察流集合,并向进程返回哪个流准备好的指令。这种能力允许进程通过利用操作系统返回的准备信息,使用通用代码和单个线程复用多个活动流。这种方式被广泛用于网络服务器,以便处理大量的网络连接。准备选择对于大容量扩展是至关重要的。
  • [新手课堂] Python爬虫的多线程使用方法
    1.构造线程构建新的线程有两种方法,一种是直接通过Thread方法构建一个线程,另一种时通过继承Thread类,重写run()方法构建。Thread(group=None,target=None,name=None,args=(),kwargs={})groups :线程组,这个暂时不用管,官方文档是这样说的:“group should be None”,所以我们可以不写就行。target :我们要使用线程的方法name :线程名args/kwargs :传给方法的参数。要注意,如果只有一个参数,元组记得加个“,”,否则就会报错。2.其他实例方法setDaemon(bool) :设置守护线程,参数默认为False,如果需要设置,则设置为True,将子线程设置成守护线程后,主线程结束,无论守护线程是否执行完毕,守护线程和主线程一起停止。看一下下面这个示例就知道了。守护线程有两种设置方法,守护线程一定要在线程执行前设置,否则无效。主任务结束后,守护线程其实还没有结束,但也一起随主线程结束了。start() :这个不用再多介绍了,就是启动线程join(timeout) :这个就是为了防止主线程直接结束,先阻塞当前进程,直到调用这个方法的进程终止,或者达到设置的可选参数timeout时限。setName(anme)/getName(name) :设置/获取线程名字isAlive() :返回线程是否在运行。enumerate() :返回正在运行的线程名activeCount() : 返回正在运行的线程数3.锁,可重入锁    具体方法如下:        acquire([timeout]): 请求获得锁定。使线程进入同步阻塞状态。        release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常。        关于线程的锁的案例,这里给出一个简单的指令锁的示例,主要看下,锁的作用。简单的线程代码如下:import timeimport threading  def spider(a):    print("启动任务")    print(f"执行的参数为{a}")    time.sleep(5)    print(f"结束任务{a}")    print('已经结束任务')  def main():    start_time = time.time()    for i in range(1,5):        s1 = threading.Thread(target=spider, args=(i,))        s1.start()        # 等待两个子线程结束再结束主线程    s1.join()    end_time = time.time()    total_time = end_time - start_time    print(f"所有任务结束,总耗时为:{total_time}") main()到这里已经简单的介绍完毕。
  • [新手课堂] Python爬虫的多线程使用方法
    1.构造线程构建新的线程有两种方法,一种是直接通过Thread方法构建一个线程,另一种时通过继承Thread类,重写run()方法构建。Thread(group=None,target=None,name=None,args=(),kwargs={})groups :线程组,这个暂时不用管,官方文档是这样说的:“group should be None”,所以我们可以不写就行。target :我们要使用线程的方法name :线程名args/kwargs :传给方法的参数。要注意,如果只有一个参数,元组记得加个“,”,否则就会报错。2.其他实例方法setDaemon(bool) :设置守护线程,参数默认为False,如果需要设置,则设置为True,将子线程设置成守护线程后,主线程结束,无论守护线程是否执行完毕,守护线程和主线程一起停止。看一下下面这个示例就知道了。守护线程有两种设置方法,守护线程一定要在线程执行前设置,否则无效。主任务结束后,守护线程其实还没有结束,但也一起随主线程结束了。start() :这个不用再多介绍了,就是启动线程join(timeout) :这个就是为了防止主线程直接结束,先阻塞当前进程,直到调用这个方法的进程终止,或者达到设置的可选参数timeout时限。setName(anme)/getName(name) :设置/获取线程名字isAlive() :返回线程是否在运行。enumerate() :返回正在运行的线程名activeCount() : 返回正在运行的线程数3.锁,可重入锁    具体方法如下:        acquire([timeout]): 请求获得锁定。使线程进入同步阻塞状态。        release(): 释放锁。使用前线程必须已获得锁定,否则将抛出异常。        关于线程的锁的案例,这里给出一个简单的指令锁的示例,主要看下,锁的作用。简单的线程代码如下:import timeimport threading  def spider(a):    print("启动任务")    print(f"执行的参数为{a}")    time.sleep(5)    print(f"结束任务{a}")    print('已经结束任务')  def main():    start_time = time.time()    for i in range(1,5):        s1 = threading.Thread(target=spider, args=(i,))        s1.start()        # 等待两个子线程结束再结束主线程    s1.join()    end_time = time.time()    total_time = end_time - start_time    print(f"所有任务结束,总耗时为:{total_time}") main()到这里已经简单的介绍完毕。
  • [新手课堂] Java线程的状态
    Java 线程有以下几个状态:新建状态(New)就绪状态(Runnable)运行状态(Running)阻塞状态(Blocked):等待阻塞同步阻塞其他阻塞死亡状态(Dead)
  • [新手课堂] JVM原理学习总结
    这篇总结主要是基于我之前JVM系列文章而形成的的。主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点。谢谢#更多详细内容可以查看我的专栏文章:深入理解JVM虚拟机https://blog.csdn.net/column/details/21960.htmlJVM介绍和源码首先JVM是一个虚拟机,当你安装了jre,它就包含了jvm环境。JVM有自己的内存结构,字节码执行引擎,因此class字节码才能在jvm上运行,除了Java以外,Scala,groovy等语言也可以编译成字节码而后在jvm中运行。JVM是用c开发的。JVM内存模型内存模型老生常谈了,主要就是线程共享的堆区,方法区,本地方法栈。还有线程私有的虚拟机栈和程序计数器。堆区存放所有对象,每个对象有一个地址,Java类jvm初始化时加载到方法区,而后会在堆区中生成一个Class对象,来负责这个类所有实例的实例化。栈区存放的是栈帧结构,栈帧是一段内存空间,包括参数列表,返回地址,局部变量表等,局部变量表由一堆slot组成,slot的大小固定,根据变量的数据类型决定需要用到几个slot。方法区存放类的元数据,将原来的字面量转换成引用,当然,方法区也提供常量池,常量池存放-128到127的数字类型的包装类。字符串常量池则会存放使用intern的字符串变量。JVM OOM和内存泄漏这里指的是oom和内存泄漏这类错误。oom一般分为三种,堆区内存溢出,栈区内存溢出以及方法区内存溢出。堆内存溢出主要原因是创建了太多对象,比如一个集合类死循环添加一个数,此时设置jvm参数使堆内存最大值为10m,一会就会报oom异常。栈内存溢出主要与栈空间和线程有关,因为栈是线程私有的,如果创建太多线程,内存值超过栈空间上限,也会报oom。方法区内存溢出主要是由于动态加载类的数量太多,或者是不断创建一个动态代理,用不了多久方法区内存也会溢出,会报oom,这里在1.7之前会报permgem oom,1.8则会报meta space oom,这是因为1.8中删除了堆中的永久代,转而使用元数据区。内存泄漏一般是因为对象被引用无法回收,比如一个集合中存着很多对象,可能你在外部代码把对象的引用置空了,但是由于对象还被集合给引用着,所以无法被回收,导致内存泄漏。测试也很简单,就在集合里添加对象,添加完以后把引用置空,循环操作,一会就会出现oom异常,原因是内存泄漏太多了,导致没有空间分配新的对象。常见调试工具命令行工具有jstack jstat jmap 等,jstack可以跟踪线程的调用堆栈,以便追踪错误原因。jstat可以检查jvm的内存使用情况,gc情况以及线程状态等。jmap用于把堆栈快照转储到文件系统,然后可以用其他工具去排查。visualvm是一款很不错的gui调试工具,可以远程登录主机以便访问其jvm的状态并进行监控。class文件结构class文件结构比较复杂,首先jvm定义了一个class文件的规则,并且让jvm按照这个规则去验证与读取。开头是一串魔数,然后接下来会有各种不同长度的数据,通过class的规则去读取这些数据,jvm就可以识别其内容,最后将其加载到方法区。JVM的类加载机制jvm的类加载顺序是bootstrap类加载器,extclassloader加载器,最后是appclassloader用户加载器,分别加载的是jdk/bin ,jdk/ext以及用户定义的类目录下的类(一般通过ide指定),一般核心类都由bootstrap和ext加载器来加载,appclassloader用于加载自己写的类。双亲委派模型,加载一个类时,首先获取当前类加载器,先找到最高层的类加载器bootstrap让他尝试加载,他如果加载不了再让ext加载器去加载,如果他也加载不了再让appclassloader去加载。这样的话,确保一个类型只会被加载一次,并且以高层类加载器为准,防止某些类与核心类重复,产生错误。defineclass findclass和loadclass类加载classloader中有两个方法loadclass和findclass,loadclass遵从双亲委派模型,先调用父类加载的loadclass,如果父类和自己都无法加载该类,则会去调用findclass方法,而findclass默认实现为空,如果要自定义类加载方式,则可以重写findclass方法。常见使用defineclass的情况是从网络或者文件读取字节码,然后通过defineclass将其定义成一个类,并且返回一个Class对象,说明此时类已经加载到方法区了。当然1.8以前实现方法区的是永久代,1.8以后则是元空间了。JVM虚拟机字节码执行引擎jvm通过字节码执行引擎来执行class代码,他是一个栈式执行引擎。这部分内容比较高深,在这里就不献丑了。编译期优化和运行期优化编译期优化主要有几种1 泛型的擦除,使得泛型在编译时变成了实际类型,也叫伪泛型。2 自动拆箱装箱,foreach循环自动变成迭代器实现的for循环。3 条件编译,比如if(true)直接可得。运行期优化主要有几种1 JIT即时编译Java既是编译语言也是解释语言,因为需要编译代码生成字节码,而后通过解释器解释执行。但是,有些代码由于经常被使用而成为热点代码,每次都编译太过费时费力,干脆直接把他编译成本地代码,这种方式叫做JIT即时编译处理,所以这部分代码可以直接在本地运行而不需要通过jvm的执行引擎。2 公共表达式擦除,就是一个式子在后面如果没有被修改,在后面调用时就会被直接替换成数值。3 数组边界擦除,方法内联,比较偏,意义不大。4 逃逸分析,用于分析一个对象的作用范围,如果只局限在方法中被访问,则说明不会逃逸出方法,这样的话他就是线程安全的,不需要进行并发加锁。1JVM的垃圾回收1 GC算法:停止复制,存活对象少时适用,缺点是需要两倍空间。标记清除,存活对象多时适用,但是容易产生随便。标记整理,存活对象少时适用,需要移动对象较多。2 GC分区,一般GC发生在堆区,堆区可分为年轻代,老年代,以前有永久代,现在没有了。年轻代分为eden和survior,新对象分配在eden,当年轻代满时触发minor gc,存活对象移至survivor区,然后两个区互换,等待下一场gc,当对象存活的阈值达到设定值时进入老年代,大对象也会直接进入老年代。老年代空间较大,当老年代空间不足以存放年轻代过来的对象时,开始进行full gc。同时整理年轻代和老年代。一般年轻代使用停止复制,老年代使用标记清除。3 垃圾收集器serial串行parallel并行它们都有年轻代与老年代的不同实现。然后是scanvage收集器,注重吞吐量,可以自己设置,不过不注重延迟。cms垃圾收集器,注重延迟的缩短和控制,并且收集线程和系统线程可以并发。cms收集步骤主要是,初次标记gc root,然后停顿进行并发标记,而后处理改变后的标记,最后停顿进行并发清除。g1收集器和cms的收集方式类似,但是g1将堆内存划分成了大小相同的小块区域,并且将垃圾集中到一个区域,存活对象集中到另一个区域,然后进行收集,防止产生碎片,同时使分配方式更灵活,它还支持根据对象变化预测停顿时间,从而更好地帮用户解决延迟等问题。
  • [交流吐槽] CEO如何制定数字化转型KPI
    编辑导读:数字化转型已经成为一种趋势,企业可以根据不同阶段设定相应的利于收益增长和战略能力提升的指标,从而推动数字化业务的发展进程。本文作者对此进行了分析,与你分享。制定数字化转型关键绩效指标(KPI),涉及如下 3 个方面的考量:如何确定数字化转型的衡量标准及原则。如何考量数字化进程中企业的收益。如何构建面向未来的战略能力。一、数字化转型 KPI 制定的标准和原则作为企业运营管理的掌舵者,CEO 在企业开始数字化转型前,需要确定数字化转型 KPI 的衡量标准及原则。譬如银行数字化转型 KPI 的制定要遵循两类原则,一类是收益类 KPI 的制定,一类是战略能力 KPI 的制定。收益类 KPI 旨在以数字化转型的方式提升银行当前及未来的收益,战略能力 KPI旨在提升银行面向未来的战略能力,比如商机捕捉能力、效率提升、数据应用能力等。无论是收益类 KPI 还是战略能力 KPI,都应该涵盖企业数字化业务发展的各个阶段。企业可以根据不同阶段设定相应的利于收益增长和战略能力提升的指标,从而推动数字化业务的发展进程。另外,应该在数字化业务发展前期规划不同阶段的目标,而不是项目实施过程中再规划,并且在前期规划时应该做到足够周全、详细,保证规划合理。最后,数字化业务指标不宜过少或过多。指标过少,在整个项目的推进过程中,工作边界会难以掌控,责权不到位;指标过多,则行动受限制,负责内容过多,影响数据团队的角色定位。数字化转型 KPI 旨在评估企业的数字化转型发展进程,继而带动业绩提升,业绩变化可以体现在企业的KPI 中。只有明确了数字化转型的衡量标准,才能对数字化业务发展进行指导,从而帮助企业根据行业和业务的不同,随时调整评估指标。但须注意的是,这些关键绩效指标不是一成不变的,它与企业的其他业绩评估指标相辅相成。二、量化各领域数字化收益在企业进行数字化转型的过程中,CEO 不仅要明确 KPI 制 定的原则,还需要与不同部门的高管合作,量化数字化将为各部门带来的收益指标。在与这些高管合作的过程中,CEO 需要制 定一套 KPI 来评估目前业务模式的数字化进程。KPI 可以帮助 CEO 衡量数智业务的效率,及时调整数字化模式。比如销售领域的数字化转型 KPI 可以帮助侧重于销售业务的企业根据数字化 渠道衡量销售比例,从而评估数字化对公司的销售业绩的帮助。CEO 可以对运营、供应链、产品、客服等企业运营管理的 不同方面设定数字化目标和 KPI。例如仓储方面,在数字化 KPI 的推动下,某企业在一定时间内,库存可由 20 亿降为 5 亿。三、构建面向未来的战略能力CEO 在制定数字化转型 KPI 时,除了要制定收益类的 KPI 外,还要制定面向未来的战略能力 KPI。这些战略能力可以帮助企业在未来赢得或保持市场份额,是企业可以持续发展的数字化能力。战略能力包含很多方面,比如捕获商机的能力、提升效率 的能力、数据应用的能力等。CEO 在制定商机捕获方面的战略能力 KPI 时,可以从对突发事件和热点事件的数据分析质量及速度、对事件的把控以及对流量红利的运用效果等方面综合考虑。CEO 在制定效率提升方面的战略能力 KPI 时,可以从获客 能力方面考虑。譬如某企业运用了数字化的获客方式后,其获客能力比竞品公司高,每个获客环节的效率都提升了,那么该企业 可以根据获客能力制定战略能力 KPI。CEO 在制定数据应用方面的战略能力 KPI 时,可以从数据 应用的质量和数量、反馈时长等方面综合考虑。譬如 CEO 可以考虑有多少人运用数据解决问题,用数据解决业务问题的效率有 多高、反应弧有多短,这些都是数据应用方面战略能力 KPI 的参考因素。战略能力在很多时候是无形的,是无法直接看到的,但战略能力会逐渐植入到企业的组织架构和业务发展中,以数字化的方式潜移默化地影响整个公司的经营发展。企业在发展过程中,短期以绩效和收益为重点,长期以战略能力为核心。四、制定 KPI 的 5 个注意事项企业在推进数字化转型的过程中,需要考量团队成员的工作效果,制定 KPI 是非常有必要的。但数字化转型 KPI 不同于企业日常运营 KPI,企业在制定时需要注意 5 个事项。1)数字化转型 KPI 不是企业KPI数字化业务目标与企业日常运营 KPI 是不同的,二者无法相互取代。二者对企业拓展业务、追求收益的目标是一致的,基本都围绕着收入增长、降低费用、增加利润几个目标而努力。不同点在于实现目标的方式,数字化业务强调运用新型数字化技术与业务相结合达到降本增效、收益递增。但企业在执行数字化业务 目标的同时还应该保持企业日常运营 KPI,借此评估数字化业务的进程和机会,即通过营收、利润指标明确体现数字化营收与非 数字化营收之间的差异。2)数字化转型 KPI 是临时的KPI 是有时限的,因此,企业在设定数字化转型 KPI 时应该建立时间范围,设定起点时间与终点时间。数字化转型的终点时间可以结合转型进度进行调整,终点时间的设定将推动团队成员按部就班地执行数字化任务。企业完成数字化转型后,原先的 KPI 可能会发生变化。比如,某些可续用的数字化指标可添加到企业 KPI 中,成为企业永久性的业务指标,指导数字化工作不断深入发展。3)数字化转型应设立平衡点指标企业数字化转型并不意味着所有业务模式都需要实现数字化。实际上,设置太多数字化互动模式可能会产生负面影响。企业设置数字化转型的 KPI 应该在员工与客户之间达到平衡,每个 KPI 都应该有一个平衡点,以避免过度数字化。而随着数字化进程的推进,平衡点也可能随之改变,企业数字化转型 KPI 的设定 应与企业业务特性等相匹配。4)多部门仅分享关键指标虽然企业数字化转型中涉及很多具体指标,但只有一部分关键指标可与多部门共享。生产过程中会涉及不同维度的指标,比如生产指标、数据分析指标、产量优化指标、消耗品减少指标、库存最大化指标,可将生产指标作为关键指标描述生产过程的数字化水平,并与多部门共享。而其他单一指标并不足以代表该项 目的数字化进程,也就无须共享了。除了要共享关键业务指标外, 在数字化转型的各个关键节点上,也应该设置明确的可衡量目标。5)制定数字化转型 KPI 应计算当前市场份额,预测未来市 场规模企业在制定数字化转型 KPI 时可能会参考企业 KPI 模式,仅考虑到跟踪增量进度和收益,但数字化业务往往可以颠覆商业模式和企业的业务模式,创造出全新的机会,因此数字化转型 KPI 的设定除了要考虑到执行进度中的目标,还应该评估企业追求创新增长机会的指标。KPI帮助 CEO 了解企业目前的营收及当前市场份额,并对未来市场发展提供参考,以便企业能够正确量化新业务模式,为将要出现的商机做充分准备。 本文由 @国云数据 原创发布于人人都是产品经理。
  • [知识分享] 【数据库系列】GaussDB(DWS)中共享消息队列实现的三大功能
    >摘要:本文将详细介绍GaussDB(DWS)中共享消息队列的实现。本文分享自华为云社区[《GaussDB(DWS)CBB组件之共享消息队列介绍》](https://bbs.huaweicloud.com/blogs/317028?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=ei&utm_content=content),作者:疯狂朔朔。 ## 1)共享消息队列是什么? 在前文中,我们讲解了SysCache的实现原理,GaussDB(DWS)通过SysCache缓存表元数据,以加速查询,然而在并发查询过程中,不可避免地会出现需要同步元数据的情况,举个简单例子,假设存在以下语句执行流程: 1. Create table abc(会话1) 2. Select * from abc(会话1) 3. Drop table abc(会话2) 4. Select * from abc(会话1) 在会话1中,会连续两次执行Select表操作(b和d),在b语句执行后,会话1将对abc的元数据进行缓存,缓存到SysCache中,以备后续使用。然而,在c语句执行后,需要对会话1中的元数据进行失效,否则,会话1将在执行d语句过程中发生错误,读取已删除的数据。 那么,会话2如何“通知”会话1失效哪些数据呢?答案是共享消息队列。 ## 2)共享消息队列存储结构 如图所示,为共享消息队列数据结构 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/105037oggabkl0enmuomrs.png) 图示中主要包括两部分,下面部分为ThreadLocal结构,主要记录的是每个线程内部的数据,线程间数据是独立的,无法互相访问,上面部分为共享消息队列中的数据,共享消息队列存在于共享内存中,可同时被多个线程访问,共享消息队列的访问场景是典型的一写多读场景。 在共享消息队列中,核心变量有三个,nextMsgNum、minMsgNum、MaxMsgNum,其中nextMsgNum记录了每个线程消息读取到的位置,minMsgNum记录了共享消息队列中最早消息的位置,maxMsgNum记录了共享消息队列中最新消息的位置,对于每个线程而言,需要定期(在表加锁/事务开始/收到信号时触发)从共享消息队列中读取失效消息,利用失效消息(共享消息队列中的每个消息)更新线程内部数据,同时,若线程内部产生失效消息(通常DDL语句在事务提交时产生大量失效消息),则需要向共享消息队列中插入失效数据,供其他线程读取。另外,还有两个参数,hasMessages、resetState,其中hasMessages用于标记对应线程是否存在未读取的失效消息,resetState用于标记对应线程是否需要失效全部消息。 NOTE:失效数据有哪些?失效消息一共有六类(源自PG),有兴趣的同学可以研读PG源码,在此处我们不再展开,仅需知道线程需要从共享消息队列中读取/插入消息,以实现数据同步。 ## 3)共享消息队列接口实现 共享消息队列本质上对于外部接口只需要提供三个功能:读取共享消息队列中消息、向共享消息队列中写入消息、清理共享消息队列。 ### 3.1)读取共享消息队列 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/105103kedkmjdimc9fbk4z.png) 如图所示,为失效消息读取过程。在线程同步失效消息过程中,有几个关键点: - 若共享内存中线程对应的hasMessage为True,则表示有失效消息需要读取,否则直接返回,无新的失效消息。 - 读取失效消息过程中,需要持有读共享锁,以保证读取的消息不会被清理掉。 - 若读取失效消息过程中,发现resetState被置为True,说明该线程已经无法使用共享消息队列中的消息进行追增,需要对缓存进行全失效。缓存全失效相当于追增全部数据,需要将nextMsgNum置为maxMsgNum。缓存全失效将严重降低SQL执行性能,尽量减少缓存全失效的发生频率。 - 在追增数据过程中,会推进线程自身的nextMsgNum,以标记数据追增位置。 NOTE:在读取共享消息队列过程中,每个线程推进自己的nextMsgNum位置,以方便记录数据追增情况。 ### 3.2)向共享消息队列中写入消息 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/105136bjshf3gdxrosxe28.png) 如图所示,为失效消息写入过程。有以下几个关键点: - 写入失效消息过程,需要调用清理共享消息队列接口,以保证有足够多的空位写入失效消息。 - 在写入失效消息过程中,会更新maxMsgNum。 - 写入失效消息完毕以后,需要将其他线程的hasMessage标记为True。 - 写入失效消息过程,需要持排他写锁,阻塞其他线程写操作,但不阻塞读。 NOTE:写入失效消息过程实际上是推进maxMsgNum的过程,同时告知其他线程有新的失效消息需要读取。 ### 3.3)清理共享消息队列 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/10520213zlw4xpfruowu9h.png) 如图所示,清理共享消息队列过程中,有以下几个关键点: - 清理共享消息队列需要持有排他读锁和排他写锁,阻塞读过程。其主要原因是,清理共享消息队列会推进minMsgNum,若不持读锁,可能导致nextMsgNum读取过期数据。 - 清理共享消息队列会推进minMsgNum。 - 清理共享消息队列过程,会将所有没有及时追增失效消息的线程执行全失效。 - 在清理共享消息队列最后步骤,会对距离minMsgNum最近的线程,发送追增信号,以确保不会频繁发生全失效。该步骤主要考虑的情况是,若线程长时间处于idle状态,需要外部信号触发其及时追增消息。 NOTE:清理共享消息队列过程,实际上是推进minMsgNum的过程,同时对所有没有及时追增失效消息的线程执行全失效。 根据以上共享消息队列接口可知,读取共享消息队列主要负责推进各个线程自身的nextMsgNum;写入失效消息主要负责推进maxMsgNum;清理共享消息队列主要负责推进minMsgNum。通过共享消息队列,可有效实现各个线程之间的数据同步。
  • [知识分享] 【微服务系列】认识容器,我们从它的历史开始聊起
    >摘要:Docker为什么火,靠的就是Docker镜像。他打包了应用程序的所有依赖,彻底解决了环境的一致性问题,重新定义了软件的交付方式,提高了生产效率。本文分享自华为云社区[《认识容器,我们从它的历史开始聊起》](https://bbs.huaweicloud.com/blogs/285728?utm_source=csdn&utm_medium=bbs-ex&utm_campaign=other&utm_content=content),作者:技术火炬手。 关于容器的历史、发展以及技术本质,在互联网上已经有非常多的文章了。这里旨在结合自身的工作经验和理解,通过一系列的文章,讲清楚这项技术。 # 容器的历史和发展 ### 1、前世 讲到容器,就不得不提LXC(Linux Container),他是Docker的前生,或者说Docker是LXC的使用者。完整的LXC能力在2008年合入Linux主线,所以容器的概念在2008年就基本定型了,并不是后面Docker造出来的。关于LXC的介绍很多,大体都会说“LXC是Linux内核提供的容器技术,能提供轻量级的虚拟化能力,能隔离进程和资源”,但总结起来,无外乎就两大知识点Cgroups(Linux Control Group)和Linux Namespace。搞清楚他俩,容器技术就基本掌握了。 - Cgroups:重点在“限制”。限制资源的使用,包括CPU、内存、磁盘的使用,体现出对资源的管理能力。 - Namespace:重点在“隔离”。隔离进程看到的Linux视图。说大白话就是,容器和容器之间不要相互影响,容器和宿主机之间不要相互影响。 ### 2、少年期起步艰难 2009年,Cloud Foundry基于LXC实现了对容器的操作,该项目取名为Warden。2010年,dotCloud公司同样基于LXC技术,使用Go语言实现了一款容器引擎,也就是现在的Docker。那时,dotCloud公司还是个小公司,出生卑微的Docker没什么热度,活得相当艰难。 ### 3、 成长为巨无霸 2013年,dotCloud公司决定将Docker开源。开源后,项目突然就火了。从大的说,火的原因就是Docker的这句口号“Build once,Run AnyWhere”。呵呵,是不是似曾相识?对的,和Java的Write Once,Run AnyWhere一个道理。对于一个程序员来说,程序写完后打包成镜像就可以随处部署和运行,开发、测试和生产环境完全一致,这是多么大一个诱惑。程序员再也不用去定位因环境差异导致的各种坑爹问题。 Docker开源项目的异常火爆,直接驱动dotCloud公司在2013年更名为Docker公司。Docker也快速成长,干掉了CoreOS公司的rkt容器和Google的lmctfy容器,直接变成了容器的事实标准。也就有了后来人一提到容器就认为是Docker。 总结起来,Docker为什么火,靠的就是Docker镜像。他打包了应用程序的所有依赖,彻底解决了环境的一致性问题,重新定义了软件的交付方式,提高了生产效率。 ### 4、 被列强蚕食 Docker在容器领域快速成长,野心自然也变大了。2014年推出了容器云产品Swarm(K8s的同类产品),想扩张事业版图。同时Docker在开源社区拥有绝对话语权,相当强势。这种走自己的路,让别人无路可走的行为,让容器领域的其他大厂玩家很是不爽,为了不让Docker一家独大,决定要干他。 2015年6月,在Google、Redhat等大厂的“运作”下,Linux基金会成立了OCI(Open Container Initiative)组织,旨在围绕容器格式和运行时制定一个开放的工业化标准,也就是我们常说的OCI标准。同时,Docker公司将Libcontainer模块捐给CNCF社区,作为OCI标准的实现,这就是现在的RunC项目。说白了,就是现在这块儿有个标准了,大家一起玩儿,不被某个特定项目的绑定。 讲到Docker,就得说说Google家的Kubernetes,他作为容器云平台的事实标准,如今已被广泛使用,俨然已成为大厂标配。Kubernetes原生支持Docker,让Docker的市场占有率一直居高不下。如图是2019年容器运行时的市场占有率。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/095520zlfv6d2vhscjg9c0.png) 但在2020年,Kubernetes突然宣布在1.20版本以后,也就是2021年以后,不再支持Docker作为默认的容器运行时,将在代码主干中去除dockershim。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/095534m3njj6fcpkafodbq.png) 如图所示,K8s自身定义了标准的容器运行时接口CRI(Container Runtime Interface),目的是能对接任何实现了CRI接口的容器运行时。在初期,Docker是容器运行时不容置疑的王者,K8s便内置了对Docker的支持,通过dockershim来实现标准CRI接口到Docker接口的适配,以此获得更多的用户。随着开源的容器运行时Containerd(实现了CRI接口,同样由Docker捐给CNCF)的成熟,K8s不再维护dockershim,仅负责维护标准的CRI,解除与某特定容器运行时的绑定。当然,也不是K8s不支持Docker了,只是dockershim谁维护的问题。 随着K8s态度的变化,预计将会有越来越多的开发者选择直接与开源的Containerd对接,Docker公司和Docker开源项目(现已改名为moby)未来将会发生什么样的变化,谁也说不好。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/095544zkpq6jusz5fbqvq1.png) 讲到这里,不知道大家有没有注意到,Docker公司其实是捐献了Containerd和runC。这俩到底是啥东西。简单的说,runC是OCI标准的实现,也叫OCI运行时,是真正负责操作容器的。Containerd对外提供接口,管理、控制着runC。所以上面的图,真正应该长这样。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/0955522szerhigfhuhet3k.png) Docker公司是一个典型的小公司因一个爆款项目火起来的案例,不管是技术层面、公司经营层面以及如何跟大厂缠斗,不管是好的方面还是坏的方面,都值得我们去学习和了解其背后的故事。 # 什么是容器 按国际惯例,在介绍一个新概念的时候,都得从大家熟悉的东西说起。幸好容器这个概念还算好理解,喝水的杯子,洗脚的桶,养鱼的缸都是容器。容器技术里面的“容器”也是类似概念,只是装的东西不同罢了,他装的是应用软件本身以及软件运行起来需要的依赖。用鱼缸来类比,鱼缸这个容器里面装的应用软件就是鱼,装的依赖就是鱼食和水。这样大家就能理解docker的logo了。大海就是宿主机,docker就是那条鲸鱼,鲸鱼背上的集装箱就是容器,我们的应用程序就装在集装箱里面。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/095613p1cmktl4clepa9ku.png) 在讲容器的时候一定绕不开容器镜像,这里先简单的把容器镜像理解为是一个压缩包。压缩包里包含应用的可执行程序以及程序依赖的文件(例如:配置文件和需要调用的动态库等),接下来通过实际操作来看看容器到底是个啥。 ## 一、宿主机视角看容器: **1、首先,我们启动容器。** `docker run -d --name="aimar-1-container" euleros_arm:2.0SP8SPC306 /bin/sh -c "while true; do echo aimar-1-container; sleep 1; done"` 这是Docker的标准命令。意思是使用euleros_arm:2.0SP8SPC306镜像(镜像名:版本号)创建一个新的名字为"aimar-1-container"的容器,并在容器中执行shell命令:每秒打印一次“aimar-1-container”。 - **参数说明:** -d:使用后台运行模式启动容器,并返回容器ID。 --name:为容器指定一个名字。 docker run -d --name="aimar-1-container" euleros_arm:2.0SP8SPC306 /bin/sh -c "while true; do echo aimar-1-container; sleep 1; done" 207b7c0cbd811791f7006cd56e17033eb430ec656f05b6cd172c77cf45ad093c 从输出中,我们看到一串长字符207b7c0cbd811791f7006cd56e17033eb430ec656f05b6cd172c77cf45ad093c。他就是容器ID,能唯一标识一个容器。当然在使用的时候,不需要使用全id,直接使用缩写id即可(全id的前几位)。例如下图中,通过docker ps查询到的容器id为207b7c0cbd81 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/095752kjcmoypnog2itjl4.png) aimar-1-container容器启动成功后,我们在宿主机上使用ps进行查看。这时可以发现刚才启动的容器就是个进程,PID为12280。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/0958020useuirw8znsxwk6.png) 我们尝试着再启动2个容器,并再次在宿主机进行查看,你会发现又新增了2个进程,PID分别为20049和21097。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/095819nfq66zfu69mxdmsa.png) 所以,我们可以得到一个结论。**从宿主机的视角看,容器就是进程。** **2、接下来,我们进入这个容器。** `docker exec -it 207b7c0cbd81 /bin/bash` docker exec也是Docker的标准命令,用于进入某个容器。意思是进入容器id为207b7c0cbd81的容器,进入后执行/bin/bash命令,开启命令交互。 - **参数说明:** -it其实是-i和-t两个参数,意思是容器启动后,要分配一个输入/输出终端,方便我们跟容器进行交互,实现跟容器的“对话”能力。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/095908rggk8nthya49r13n.png) 从hostname从kwephispra09909变化为207b7c0cbd81,说明我们已经进入到容器里面了。在容器中,我们尝试着启动一个新的进程。 `[root@207b7c0cbd81 /]# /bin/sh -c "while true; do echo aimar-1-container-embed; sleep 1; done" &` ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/095928aov19uyf1npegag6.png) 再次回到宿主机进行ps查看,你会发现**不管是直接启动容器,还是在容器中启动新的进程,从宿主机的角度看,他们都是进程。** # 二、容器视角看容器: 前面我们已经进入容器里面,并启动了新的进程。但是我们并没有在容器里查看进程的情况。在容器中执行ps,会发现得到的结果和宿主机上执行ps的结果完全不一样。下图是容器中的执行结果。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/100019zgemqpa2wms49vjp.png) 在Container1容器中只能看见刚起启动的shell进程(container1和container1-embed),看不到宿主机上的其他进程,也看不到Container2和Container3里面的进程。这些进程像被关进了一个盒子里面,完全感知不到外界,甚至认为我们执行的container1是1号进程(1号进程也叫init进程,是系统中所有其他用户进程的祖先进程)。所以,**从容器的视角,容器觉得“我就是天,我就是地,欢迎来到我的世界”。** ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/100037isou8f0ckdd3a4ym.png) 但尴尬的是,在宿主机上,他们却是普通得不能再普通的进程。注意,相同的进程,在容器里看到的进程ID和在宿主机上看到的进程ID是不一样的。容器中的进程ID分别是1和1859,宿主机上对应的进程ID分别是12280和9775(见上图)。 # 三、总结 通过上面的实验,对容器的定义就需要再加上一个定语。**容器就是进程=>容器是与系统其他部分隔离开的进程。** 这个时候我们再看下图就更容易理解,容器是跑在宿主机OS(虚机容器的宿主机OS就是Guest OS)上的进程,容器间以及容器和宿主机间存在隔离性,例如:进程号的隔离。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/100055khovfhnu82plkzpn.png) 在容器内和宿主机上,同一个进程的进程ID不同。例如:Container1在容器内PID是1,在宿主机上是12280。那么该进程真正的PID是什么呢?当然是12280!那为什么会造成在容器内看到的PID是1呢,造成这种幻象的,正是Linux Namespace。 Linux Namespace是Linux内核用来隔离资源的方式。每个Namespace下的资源对于其他Namespace都是不透明,不可见的。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/100116dqlwj6k4swul8c4j.png) Namespace按隔离的资源进行分类: ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/1001247spdfg2xwptwtqxa.png) 前面提到的容器内外,看到的进程ID不同,正是使用了PID Namespace。那么这个Namespace在哪呢?在Linux上一切皆文件。是的,这个Namespace就在文件里。在宿主机上的proc文件中(/proc/进程号/ns)变记录了某个进程对应的Namespace信息。如下图,其中的数字(例如:pid:[ 4026534312])则表示一个Namespace。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/1001348napuwckyqgilkdr.png) 对于Container1、Container2、Container3这3个容器,我们可以看到,他们的PID Namespace是不一样的。说明他们3个容器中的PID相互隔离,也就是说,这3个容器里面可以同时拥有PID号相同的进程,例如:都有PID=1的进程。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/100143mbezjyp3t6zddk19.png) 在一个命名空间中,那这俩进程就相互可见,只是PID与宿主机上看到的不同而已。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/202112/24/100151grurjkq7cfkodbxq.png) 至此,我们可以对容器的定义再细化一层。**容器是与系统其他部分隔离开的进程=》容器是使用Linux Namespace实现与系统其他部分隔离开的进程。**
  • [技术干货] Tomcat 如何优化?
    优化连接配置.这里以tomcat7的参数配置为例,需要修改conf/server.xml文件,修改连接数,关闭客户端dns查询。参数解释:URIEncoding=”UTF-8″ :使得tomcat可以解析含有中文名的文件的url,真方便,不像apache里还有搞个mod_encoding,还要手工编译。maxSpareThreads: 如果空闲状态的线程数多于设置的数目,则将这些线程中止,减少这个池中的线程总数。minSpareThreads: 最小备用线程数,tomcat启动时的初始化的线程数。enableLookups: 这个功效和Apache中的HostnameLookups一样,设为关闭。connectionTimeout : connectionTimeout为网络连接超时时间毫秒数。maxThreads : maxThreads Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数,即最大并发数。acceptCount : acceptCount是当线程数达到maxThreads后,后续请求会被放入一个等待队列,这个acceptCount是这个队列的大小,如果这个队列也满了,就直接refuse connection。maxProcessors与minProcessors: 在 Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最 大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。通常Windows是1000个左右,Linux是2000个左右。
  • [技术干货] SPECjvm2008测试过程出现startup.compiler.sunflow堵塞一直卡住问题解决办法
    【问题现象】SPECjvm2008测试过程中startup.compiler.sunflow堵塞一直卡住问题,执行java -jar SPECjvm2008.jar -base -ikv后卡在如下界面【解决方法】执行ps -ef | grep sunflow查看对应进程ID执行cd /proc/33494/task进入对应进程ID的task路径注:33494为sunflow的PID,需根据实际系统情况调整;依次执行如下命令,显示当前进程正在执行的系统调用cd 33494cat syscallcat fd/2可查看到startup.compiler.sunflow项已经执行成功:
  • [新手课堂] 进程间的通信方式有哪些?
    管道:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。命名管道FIFO:未命名的管道只能在两个相关的进程之间通信,通过命名管道FIFO,不相关的进程也能交换数据。消息队列:消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。消息队列允许一个或多个进程向它写入与读取消息。管道和命名管道的通信数据都是先进先出原则,消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取,比FIFO更有优势。共享内存:共享内存是允许一个或多个进程共享的一块内存区域。信号量:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。
  • [新手课堂] Java线程的状态
    Java 线程有以下几个状态:新建状态(New)就绪状态(Runnable)运行状态(Running)阻塞状态(Blocked):等待阻塞同步阻塞其他阻塞死亡状态(Dead)
  • [新手课堂] 释放占用端口
    1. 找到系统当前所有的端口使用 netstat 命令查找本机各端口的网络连接情况~]netstat -nulpt #结果如下Active Internet connections (only servers)Proto Recv-Q Send-Q Local Address           Foreign Address         Statetcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTENtcp        0      0 0.0.0.0:1997            0.0.0.0:*               LISTENtcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTENtcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTENtcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTENtcp        0      0 127.0.0.1:9000          0.0.0.0:*               LISTENtcp6       0      0 :::80                   :::*                    LISTENtcp6       0      0 :::22                   :::*                    LISTENtcp6       0      0 ::1:25                  :::*                    LISTENtcp6       0      0 :::3306                 :::*                    LISTEN这里我们要找的是 80与443 端口2. 找到对应端口在系统中的进程 ID(PID)依据查找到的 1997 端口找到对应进程, lsof -i :1997, 注意 : 冒号不要漏掉了lsof -i:80 #结果如下COMMAND    PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAMEtke-gatew 6360 root   10u  IPv6 6410452      0t0  TCP *:http (LISTEN)[root@VM-0-114-centos ~]# lsof -i:443COMMAND     PID USER   FD   TYPE  DEVICE SIZE/OFF NODE NAMEtke-gatew  7627 root    9u  IPv6 7717644      0t0  TCP *:https (LISTEN).........->172.80.31.2:https (ESTABLISHED)galaxy    25638 root    8u  IPv4 6276795      0t0  TCP  <机密省略>:57802-..........3. 使用 kill -9 [PID] 命令结束进程通过 lsof 命令我们找到了进程的 PID: 29416,接下来就是使用 kill -9 [PID] 把进程结束就好了kill -9 6360kill -9 25638kill -9 7627到这里就 OK 了,不过为了保险起见,再次执行 netstat -tln 确认是否结束了端口占用 前情提要在布置k8s的时候发现容器报错如~]kubectl get pods -A -o wide.....pot   nginx-ingress-nginx-ingress-85d747cfb4-9hk72   0/1   CrashLoopBackOff.....然后想describe查看了一下报错如下~]kubectl describe pod -n pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72......... Warning  BackOff  3m14s (x613 over 137m)  kubelet  Back-off restarting failed container最后logs查看发现端口被占用~]# kubectl logs pods -n pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72Error from server (NotFound): pods "pods" not found[root@VM-0-114-centos ~]# kubectl logs  -n pot nginx-ingress-nginx-ingress-85d747cfb4-9hk72I1104 10:51:46.738108       1 main.go:169] Starting NGINX Ingress controller Version=1.6.3 GitCommit=b9378d562021/11/04 10:51:46 [emerg] 19#19: bind() to 0.0.0.0:80 failed (98: Address already in use)nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)2021/11/04 10:51:46 [emerg] 19#19: bind() to 0.0.0.0:443 failed (98: Address already in use).......2021/11/04 10:51:46 [notice] 19#19: try again to bind() after 500ms2021/11/04 10:51:46 [emerg] 19#19: still could not bind()nginx: [emerg] still could not bind()运用了以上步骤之后回复正常pot   nginx-ingress-nginx-ingress-85d747cfb4-9hk72   1/1     Running
总条数:756 到第
上滑加载中