ubuntu24.04 php7.4.33编译安装扩展openssl.so

0.ubuntu24.04 下默认安装的是 openssl3.0,php7.4.33只支持openssl1.1

1.下载OpenSSL 1.1.1w源码,编译安装,本人安装在/usr/local/ssl

2.进入到php-7.4.33/ext/openssl下,执行 phpize
如果出现Cannot find config.m4;可以把config0.m4改为config.m4
export PKG_CONFIG_PATH=/usr/local/ssl/lib/pkgconfig
export CPPFLAGS=-I/usr/local/ssl/include
export LDFLAGS=-L/usr/local/ssl/lib
export OPENSSL_CONF=/usr/local/ssl/openssl.cnf

./configure --with-openssl=/usr/local/ssl
make -j && make install

3.配置扩展证书:openssl.capath=/usr/lib/ssl/certs

JAVA运算符重载实现方法

最近有很多数据运算方面的需求,有很多对象之间运算,BigDecimal只能用方法进行运算,当有多个计算时候阅读很不方便.

考虑是否有类似 C++和运算符重载方法,但是 Java 本身不支持运算符重载,在查找资料中发现了一个工具manifold,在编译前加插件来实现运算符重载的功能,相当是一种语法糖,让对象之间可以使用运算符

当然manifold还提供了一些其他强大的扩展功能,但是不建议在正式的项目中使用,做一些试验的功能还是可以的,减少编码量,快速验证一些功能

windows Traceback (most recent call last): File “d:\python\lib\runpy.py“

错误:pip报No module named ‘pip’错怎么处理?

有时候在执行pip更新失败后,再次执行pip命令时会提示ModuleNotFoundError: No module named ‘pip’错误,导致pip命令无法使用。

重新打开一个cmd命令窗口,选择使用管理员权限打开:可以直接右键或是点击右侧功能,以管理员身份运行;

当在执行pip更新时,出现失败或警告,再次执行pip命令报错时,分别按顺序执行以下2条命令即可完成修复。
python -m ensurepip
python -m pip install –upgrade pip

Java Synchronized 关键字

Synchronized是基于monitor实现的,Synchronized经过编译后,会在同步块前后分别形成monitorenter和monitorexit两个字节码指令,在执行monitorenter指令时,首先要尝试获取对象锁,如果对象没有别锁定,或者当前已经拥有这个对象锁,把锁的计数器加1,相应的在执行monitorexit指令时,会将计数器减1,当计数器为0时,锁就被释放了。
如果获取锁失败,那当前线程就要阻塞,直到对象锁被另一个线程释放为止。

synchronized底层语义原理

Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。
同步方法 并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的,关于这点,稍后详细分析。

下面先来了解一个概念Java对象头,这对深入理解synchronized实现原理非常关键。

synchronized特点:可重入锁/排他锁/属于jvm,由jvm实现
可重入锁实现可重入性原理或机制是:
每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;
当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为 0,则释放该锁。

synchronized保证可见性
JMM关于synchronized的两条规定:
1)线程解锁前,必须把共享变量的最新值刷新到主内存中
2)线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值
锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级。

在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。

实例变量:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。
填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐,这点了解即可。
Java头对象,它实现synchronized的锁对象的基础,synchronized使用的锁对象是存储在Java对象头里的,jvm中采用2个字来存储对象头(如果对象是数组则会分配3个字,多出来的1个字记录的是数组长度),其主要结构是由Mark Word 和 Class Metadata Address 组成

轻量级锁和偏向锁是Java 6 对 synchronized 锁进行优化后新增加的,重量级锁也就是通常说synchronized的对象锁,锁标识位为10,其中指针指向的是monitor对象(也称为管程或监视器锁)的起始地址。
每个对象都存在着一个 monitor 与之关联,对象与其 monitor 之间的关系有存在多种实现方式,如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个 monitor 被某个线程持有后,它便处于锁定状态。
在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的)

ObjectMonitor() {
    _header       = NULL;
    _count        = 0; //记录个数
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL; //处于wait状态的线程,会被加入到_WaitSet
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ; //处于等待锁block状态的线程,会被加入到该列表
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

ObjectMonitor中有两个队列,_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表( 每个等待锁的线程都会被封装成ObjectWaiter对象),_owner指向持有ObjectMonitor对象的线程,当多个线程同时访问一段同步代码时。
首先会进入 _EntryList 集合,当线程获取到对象的monitor后进入_Owner区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1,若线程调用 wait() 方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入 WaitSet集合中等待被唤醒。
若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)。

monitor对象存在于每个Java对象的对象头中(存储的指针的指向),synchronized锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因,同时也是notify/notifyAll/wait等方法存在于顶级对象Object中的原因

JDK 14 正式发布,新特性关注

2020年3月17日JDK 14 正式发布了,其中还是有一些值得关注的新特性。如果你觉得我写的东西对于您有帮助,希望得到您的关注!

一、instanceof的模式匹配:

Instanceof是java中用于检查对象引用是否为给定Type类型的实例,并返回布尔值。在Java 14之前,我们在完成判断之后,经常需要做一下类型的强制转换,如下:

if (obj instanceof String) {
    String str = (String) obj; // 需要强转
    .. str.contains(..) ..
}else{
     ....
}

Java 14增强功能特性:

if (!(obj instanceof String str)) {
    .. str.contains(..) .. // 不再需要转换代码,实际发生了转换
} else {
    //.. str.... 这里 str 会是未定义
}

更多示例:

if (obj instanceof String str && str.length() > 5) {.. str.contains(..) ..}

if (obj instanceof String str || str.length() > 5) {.. str.contains(..) ..}

注意:仅当object不为null时,instanceOf才会匹配,然后仅将其分配给str。在instanceof中使用模式匹配可以减少Java程序中显式强制转换的数量。

二、NullPointerExceptions(注意带s)

在java 14之前,我们经常在调试代码的时候,发现一行代码中有多个对象,抛出异常之后你也无法确定到底是哪个对象为null。假设此代码中出现一个NullPointerException:

 a.b.c.i = 0;

//下面是异常信息
Exception in thread "main" java.lang.NullPointerException
                            at Prog.main(Prog.java:5)

文件名和行号不能精确指出哪个变量为空。是a还是b或c?JDK14对此做了改进。

Exception in thread "main" java.lang.NullPointerException:
        Cannot read field 'c' because 'a.b' is null.
    at Prog.main(Prog.java:5)

但是,这也存在一些风险。空指针异常消息包含源代码中的变量名。暴露此信息可能被视为程序的安全风险。

三、switch表达式

在Java 14之前*

switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        System.out.println(6);
        break;
    case TUESDAY:
        System.out.println(7);
        break;
    case THURSDAY:
    case SATURDAY:
        System.out.println(8);
        break;
    case WEDNESDAY:
        System.out.println(9);
        break;
}

Java 14增强功能

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}

四、record

Java 14使用record关键字来减少类声明语法,这有点像lombok。我们有时候需要编写许多低价值的重复代码来实现一个简单的数据载体类:构造函数,访问器,equals(),hashCode(),toString()等。为了避免这种重复代码,Java 14推出record。 java14之前的代码:

final class Point {
    public final int x;
    public final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    // 很多的equals, hashCode, toString,getters、setters
}

Java 14中就用这一行代码

record Point(int x, int y) { }

五、文本块

在Java中,要将HTML,XML,SQL或JSON代码片段嵌入到代码中通常很难阅读,为了克服此问题,Java 14引入了Text Block。 java14之前,没有文本块的HTML示例

String html = "<html>\n" +
              "    <body>\n" +
              "        <p>Hello, world</p>\n" +
              "    </body>\n" +
              "</html>\n";

java14,带文本块的HTML示例,下面的代码看上去是多行的,实际上字符串的拼接结果仍然是一行的。

String html = """
              <html>
                  <body>
                      <p>Hello, world</p>
                  </body>
              </html>
              """;

如果你希望字符串中有换行,在每行的行尾加上“\”,这样字符串就是换行的了,可以打印出来看一下。

String html = """
              <html>  \
                  <body>  \
                      <p>Hello, world</p>  \
                  </body>  \
              </html>
              """;

PHP正则表达式preg_match_all修饰符

模式修饰符

下面列出了当前可用的 PCRE 修饰符。括号中提到的名字是 PCRE 内部这些修饰符的名称。 模式修饰符中的空格,换行符会被忽略,其他字符会导致错误。i (PCRE_CASELESS)如果设置了这个修饰符,模式中的字母会进行大小写不敏感匹配。

m (PCRE_MULTILINE)默认情况下,PCRE 认为目标字符串是由单行字符组成的(然而实际上它可能会包含多行), “行首”元字符 (^) 仅匹配字符串的开始位置, 而”行末”元字符 ($) 仅匹配字符串末尾, 或者最后的换行符(除非设置了 D 修饰符)。这个行为和 perl 相同。 当这个修饰符设置之后,“行首”和“行末”就会匹配目标字符串中任意换行符之前或之后,另外, 还分别匹配目标字符串的最开始和最末尾位置。这等同于 perl 的 /m 修饰符。如果目标字符串 中没有 “\n” 字符,或者模式中没有出现 ^ 或 $,设置这个修饰符不产生任何影响。

s (PCRE_DOTALL)如果设置了这个修饰符,模式中的点号元字符匹配所有字符,包含换行符。如果没有这个 修饰符,点号不匹配换行符。这个修饰符等同于 perl 中的/s修饰符。 一个取反字符类比如 [^a] 总是匹配换行符,而不依赖于这个修饰符的设置。

x (PCRE_EXTENDED)如果设置了这个修饰符,模式中的没有经过转义的或不在字符类中的空白数据字符总会被忽略, 并且位于一个未转义的字符类外部的#字符和下一个换行符之间的字符也被忽略。 这个修饰符 等同于 perl 中的 /x 修饰符,使被编译模式中可以包含注释。 注意:这仅用于数据字符。 空白字符 还是不能在模式的特殊字符序列中出现,比如序列 (?( 引入了一个条件子组(译注: 这种语法定义的 特殊字符序列中如果出现空白字符会导致编译错误。 比如(?(就会导致错误)。e (PREG_REPLACE_EVAL)

Warning
This feature was DEPRECATED in PHP 5.5.0, and REMOVED as of PHP 7.0.0.如果设置了这个被弃用的修饰符, preg_replace() 在进行了对替换字符串的 后向引用替换之后, 将替换后的字符串作为php 代码评估执行(eval 函数方式),并使用执行结果 作为实际参与替换的字符串。单引号、双引号、反斜线(\)和 NULL 字符在 后向引用替换时会被用反斜线转义.

This feature was DEPRECATED in PHP 5.5.0, and REMOVED as of PHP 7.0.0.如果设置了这个被弃用的修饰符, preg_replace() 在进行了对替换字符串的 后向引用替换之后, 将替换后的字符串作为php 代码评估执行(eval 函数方式),并使用执行结果 作为实际参与替换的字符串。单引号、双引号、反斜线(\)和 NULL 字符在 后向引用替换时会被用反斜线转义.

Note:

仅 preg_replace() 使用此修饰符,其他 PCRE 函数忽略此修饰符。

A (PCRE_ANCHORED)如果设置了这个修饰符,模式被强制为”锚定”模式,也就是说约束匹配使其仅从 目标字符串的开始位置搜索。这个效果同样可以使用适当的模式构造出来,并且 这也是 perl 种实现这种模式的唯一途径。

D (PCRE_DOLLAR_ENDONLY)如果这个修饰符被设置,模式中的元字符美元符号仅仅匹配目标字符串的末尾。如果这个修饰符 没有设置,当字符串以一个换行符结尾时, 美元符号还会匹配该换行符(但不会匹配之前的任何换行符)。 如果设置了修饰符m,这个修饰符被忽略. 在 perl 中没有与此修饰符等同的修饰符。S当一个模式需要多次使用的时候,为了得到匹配速度的提升,值得花费一些时间 对其进行一些额外的分析。如果设置了这个修饰符,这个额外的分析就会执行。当前, 这种对一个模式的分析仅仅适用于非锚定模式的匹配(即没有单独的固定开始字符)。

U (PCRE_UNGREEDY)这个修饰符逆转了量词的”贪婪”模式。 使量词默认为非贪婪的,通过量词后紧跟? 的方式可以使其成为贪婪的。这和 perl 是不兼容的。 它同样可以使用 模式内修饰符设置 (?U)进行设置, 或者在量词后以问号标记其非贪婪(比如.*?)。

Note:

在非贪婪模式,通常不能匹配超过 pcre.backtrack_limit 的字符。

X (PCRE_EXTRA)这个修饰符打开了 PCRE 与 perl 不兼容的附件功能。模式中的任意反斜线后就 ingen 一个 没有特殊含义的字符都会导致一个错误,以此保留这些字符以保证向后兼容性。 默认情况下,在 perl 中,反斜线紧跟一个没有特殊含义的字符被认为是该字符的原文。 当前没有其他特性由这个修饰符控制。

J (PCRE_INFO_JCHANGED)内部选项设置(?J)修改本地的PCRE_DUPNAMES选项。允许子组重名, (译注:只能通过内部选项设置,外部的 /J 设置会产生错误。)

u (PCRE_UTF8)此修正符打开一个与 perl 不兼容的附加功能。 模式和目标字符串都被认为是 utf-8 的。 无效的目标字符串会导致 preg_* 函数什么都匹配不到; 无效的模式字符串会导致 E_WARNING 级别的错误。 PHP 5.3.4 后,5字节和6字节的 UTF-8 字符序列被考虑为无效(resp. PCRE 7.3 2007-08-28)。 以前就被认为是无效的 UTF-8。

 (PCRE_INFO_JCHANGED)内部选项设置(?J)修改本地的PCRE_DUPNAMES选项。允许子组重名, (译注:只能通过内部选项设置,外部的 /J 设置会产生错误。)u (PCRE_UTF8)此修正符打开一个与 perl 不兼容的附加功能。 模式和目标字符串都被认为是 utf-8 的。 无效的目标字符串会导致 preg_* 函数什么都匹配不到; 无效的模式字符串会导致 E_WARNING 级别的错误。 PHP 5.3.4 后,5字节和6字节的 UTF-8 字符序列被考虑为无效(resp. PCRE 7.3 2007-08-28)。 以前就被认为是无效的 UTF-8。

u (PCRE_UTF8)此修正符打开一个与 perl 不兼容的附加功能。 模式和目标字符串都被认为是 utf-8 的。 无效的目标字符串会导致 preg_* 函数什么都匹配不到; 无效的模式字符串会导致 E_WARNING 级别的错误。 PHP 5.3.4 后,5字节和6字节的 UTF-8 字符序列被考虑为无效(resp. PCRE 7.3 2007-08-28)。 以前就被认为是无效的 UTF-8。

修饰符意义

/ regexp / i
不区分大小写的匹配

/ regexp / s
使句点( . )匹配任何字符,包括换行符( )

/ regexp / x
从模式中删除空白符和注释

/ regexp / m
使 ^ 匹配换行符 ( )之后的内容,美元符号($)匹配换行符 ( )之前的内容

/ regexp / e
如果替换字符串是PHP代码,使用eval()执行该代码来得到实际的替换字符串。

PHP的Perl兼容正则表达式函数也支持在Perl中不支持的其他修饰符

/ regexp / U
颠倒子模式的贪婪性; * 和 + 尽可能少地匹配而不是尽可能多。

/ regexp / u
把模式字符串当作UTF – 8编码对待

/ regexp / X
如果一个反斜杠之后跟着没有特殊意义的字符,将产生一个错误

/ regexp / A
把锚定位在字符串的开头就像模式中有 ^ 一样

/ regexp / D
使 $字符仅匹配一行的末尾

/ regexp / S
使表达式解析器更加小心地检查模式的结构,使得第二次运行时(如在一个循环中)加快速度

根据xml生成xsd再生成 javabean

JAXB官网:http://jaxb.java.net/

API链接 :http://jaxb.java.net/nonav/2.2.5/docs/api/

文档 :http://jaxb.java.net/tutorial/

  1. 利用trang.jar将Xml 转换成xsd
    Trang 是一种命令行的可以XML生成XSD的工具,生成XML文件对应的XSD文件只需要3个简单的步骤:下载地址:https://github.com/relaxng/jing-trang/release
  2. java -jar trang.jar data.xml data.xsd (trang.jar 接受两个参数,第一个为源文件,第二个为目标文件)
  3. Jaxb将xsd转成Bean;xjc 为 JDK 自带工具
    进入到你的xsd的文件夹下:
    xjc -p 包的路径 xsd的名字.xsd -d 目标的文件夹

Java GC Root

GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、栈和本地方法区不被GC所管理,因而选择这些区域内的对象作为GC roots,被GC roots引用的对象不被GC回收。

GC Root

常说的GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC roots且没有被GC roots引用的对象。

一个对象可以属于多个root,GC root有几下种:

  • Class – 由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的,他们可以以静态字段的方式保存持有其它对象。我们需要注意的一点就是,通过用户自定义的类加载器加载的类,除非相应的java.lang.Class实例以其它的某种(或多种)方式成为roots,否则它们并不是roots,.
  • Thread – 活着的线程
  • Stack Local – Java方法的local变量或参数
  • JNI Local – JNI方法的local变量或参数
  • JNI Global – 全局JNI引用
  • Monitor Used – 用于同步的监控对象
  • Held by JVM – 用于JVM特殊目的由GC保留的对象,但实际上这个与JVM的实现是有关的。可能已知的一些类型是:系统类加载器、一些JVM知道的重要的异常类、一些用于处理异常的预分配对象以及一些自定义的类加载器等。然而,JVM并没有为这些对象提供其它的信息,因此需要去确定哪些是属于”JVM持有”的了。

看java 代码的时候,很多人都会看见用完一个树或者链表后 head = null;这样的代码。有人说为了加快gc,这里为你揭秘为什么这么写。
代码一定要跑,别的不重要。
代码一定要跑,别的不重要。
代码一定要跑,别的不重要。

  • 首先来看看名词的含义,GC(Garbage Collection )
  • root 根,这个root是不是很熟悉,root用户,数据结构中树的顶级节点
    也叫 root。
  • roots 自然就是多个root了。
  • 从我们熟悉的root里有两个概念,一个是用户,一个是节点,
  • 在GC 里的含义和树里的含义差不多,开始或者顶级的节点。
    概念大概就这样,然后我们看看怎么去理解这个东西。
  • 不知道或者不熟悉的同学可以看这里 点这里了解树

怎么遍历这棵树这里就不阐述了。这里的每个节点你可以理解成每个类里的属性。

下面开始做理解gc roots

这里摘抄R大的一些回答

root大概有这些
所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。
VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。
JNI handles,包括global handles和local handles(看情况)
所有当前被加载的Java类(看情况)
Java类的引用类型静态变量(看情况)
Java类的运行时常量池里的引用类型常量(String或Class类型)(看情况)
String常量池(StringTable)里的引用
所有Java线程当前活跃的栈帧,静态引用等指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。

假设hashMap 在扩容的时候,正巧发生了gc,在标记阶段,要找出所有的root,这里的 oldTab 就是root,通过这个root,然后遍历他的所有属性,如果它里面的内容不为null 我们继续遍历内容里的对象字段是否为null,标记出所有不为null的节点。继续下图,子节点

静态引用这样的代码也会成为root

private static Object objoct;
这里可以看到Node里还有Node,标记的时候就这这样,遍历每个属性的子属性,一直到遍历完,则这些被标记的为有引用的。gc标记的过程就是一棵树在遍历他所有的节点,这样应该很清楚了吧。最后没有被标记的就给清楚掉。
/**
 * @author 铁拳阿牛
 * @createTime 2018/7/14 下午3:33
 **/
public class Node {

    private Object object;
       //如果这个Node被引用他的属性又被引用,属性为对象且不为null,这样一直遍历下去,
     
    private Node node;
}

理论扯完了,接下来实践


/**
 * @author 铁拳阿牛
 * @createTime 2018/7/14 下午3:46
 *
 * 请把以下参数设置到jvm里
 * -Xmx4000M -Xms4000M -Xmn1300M  -XX:+UseParNewGC  -XX:+UseConcMarkSweepGC  -XX:+UseCMSInitiatingOccupancyOnly  -XX:CMSInitiatingOccupancyFraction=75 -XX:+PrintGCDetails
 **/
public class Test {

    private static final int _1MB = 1024 * 1024;

    private static final int LENGTH = 40000000;

    public static void main(String[] args) {
        Node next = null;
        for(int i = 0; i <= LENGTH; i++){
            Node node = new Node(i,next);
            next = node;
        }
        //如果不设置为null这里将会又大批量的遍历,打开这里和不打开这里,gc时间完全不一样,现在你直到为什么要设置为null了吗?
//        next = null;
        triggerGC();
    }

    /**
     * 不触发gc看不见效果
     * new 很多小对象。不然直接到 old区去了。
     */
    private static void triggerGC(){
//        byte[] all = new byte[2000 * _1MB]; //这里为什么没又直接new 一个大对象?它可能直接就到old区去了。
        for(int i = 0 ; i < 500 ; i++){
            byte[] bytes = new byte[2 * _1MB];
        }
    }

    //POJO 不用看这里
    static class Node {
        
        private int valuel;
        
        private Node node;
        
        public Node(int valuel, Node node) {
            this.valuel = valuel;
            this.node = node;
        }

        public int getValuel() {
            return valuel;
        }

        public void setValuel(int valuel) {
            this.valuel = valuel;
        }

        public Node getNode() {
            return node;
        }

        public void setNode(Node node) {
            this.node = node;
        }
    }

}

第一次我们要把eden 的空间尽可能的占满将这两行代码注释掉,发现eden已经用了94% 很不错。

//        next = null;
//        triggerGC();
~~~~~~~~~~~~~~~~~
Heap
 par new generation   total 1198080K, used 1001063K [0x00000006c6000000, 0x0000000717400000, 0x0000000717400000)
  eden space 1064960K,  94% used [0x00000006c6000000, 0x0000000703199e78, 0x0000000707000000)
  from space 133120K,   0% used [0x0000000707000000, 0x0000000707000000, 0x000000070f200000)
  to   space 133120K,   0% used [0x000000070f200000, 0x000000070f200000, 0x0000000717400000)
 concurrent mark-sweep generation total 2764800K, used 0K [0x0000000717400000, 0x00000007c0000000, 0x00000007c0000000)
 Metaspace       used 2674K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 288K, capacity 386K, committed 512K, reserved 1048576K

然后我们开始测试了,发现gc时间非常的短, [Times: user=0.04 sys=0.00, real=0.01 secs] ,gc基本没有占用我们的时间。

        next = null;
        triggerGC();
~~~~~~~~~~~~~~~~~
[GC (Allocation Failure) [ParNew: 1063732K->399K(1198080K), 0.0101658 secs] 1063732K->399K(3962880K), 0.0102629 secs] [Times: user=0.04 sys=0.00, real=0.01 secs] 
Heap
 par new generation   total 1198080K, used 933048K [0x00000006c6000000, 0x0000000717400000, 0x0000000717400000)
  eden space 1064960K,  87% used [0x00000006c6000000, 0x00000006feeca630, 0x0000000707000000)
  from space 133120K,   0% used [0x000000070f200000, 0x000000070f263d38, 0x0000000717400000)
  to   space 133120K,   0% used [0x0000000707000000, 0x0000000707000000, 0x000000070f200000)
 concurrent mark-sweep generation total 2764800K, used 0K [0x0000000717400000, 0x00000007c0000000, 0x00000007c0000000)
 Metaspace       used 2674K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 288K, capacity 386K, committed 512K, reserved 1048576K

然后我们对比测试,发现gc时间非常的短,[Times: user=145.85 sys=1.89, real=20.17 secs] ,额这个gc时间。。。

//        next = null;
        triggerGC();
~~~~~~~~~~~~~~~~~
[GC (Allocation Failure) [ParNew: 1063732K->133119K(1198080K), 20.1710025 secs] 1063732K->938404K(3962880K), 20.1710585 secs] [Times: user=145.85 sys=1.89, real=20.17 secs] 
Heap
 par new generation   total 1198080K, used 1065769K [0x00000006c6000000, 0x0000000717400000, 0x0000000717400000)
  eden space 1064960K,  87% used [0x00000006c6000000, 0x00000006feeca630, 0x0000000707000000)
  from space 133120K,  99% used [0x000000070f200000, 0x00000007173ffff8, 0x0000000717400000)
  to   space 133120K,   0% used [0x0000000707000000, 0x0000000707000000, 0x000000070f200000)
 concurrent mark-sweep generation total 2764800K, used 805284K [0x0000000717400000, 0x00000007c0000000, 0x00000007c0000000)
 Metaspace       used 2676K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 288K, capacity 386K, committed 512K, reserved 1048576K

把这些对象标记后其他的都是要清除的,所以现在知道为什么要设置为null
了吗?就是为了加快标记阶段。如果你有个超大的map或者list的时候这样
做会有一点点帮助。(普通开发过程中基本没有必要考虑)

明白java的源码里为什么会有这些操作了吗?

编程语言简史:给C语言做个演示程序,结果他们弄出了一个操作系统UNIX

编程语言有上千种,但是流行的不过10来种,那些我们经常使用的编程语言都是谁在什么时候创造出来的呢?Casper Beyer 为我们进行了整理。

1800年

Joseph Marie Jacquard 教会了一台织布机读穿孔卡片,制造出了第一个高度多线程的处理单元。他的发明受到了预见天网(Skynet)诞生的纺织工人的强烈反对。

1842年

Ada Lovelace(英国诗人拜伦之女)为计算程序拟定“算法”,写作的第一份“程序设计流程图”,被珍视为“第一位给计算机写程序的人”。稍微有点不便的是当时还没有计算机呢。

1936年

阿兰·图灵被称为计算机科学之父,人工智能之父。但英国法庭却并不认可,还判处对他进行化学阉割。

女皇后来宽恕了他,但不幸的是当时他已经过世很久了。

1936年

Alonzo Church(算法理论重要奠基人)发明了lambda算子,跟图灵生活在同样的时代,但是他在时代的另一边,也并没有被女王阉割。

1957年

John Backus创建了FORTRAN语言,这真正是程序员使用的第一种语言。

1959年

Grace Hopper发明了第一门针对企业面向商业的编程语言,并且把这门语言叫做“面向商业的通用语言(common business-oriented language)”,简称COBOL。

1964年

John Kemeny 和 Thomas Kurtz 认为编程太难了,需要回归本源,他们把自己的编程语言叫做BASIC。

1970年

Niklaus Wirth开发了多种语言,最后流行起来的是PASCAL。他喜欢开发语言。

他还发明了让摩尔定律变得过时的Wirth定律(软件变慢的速度比硬件变快的速度更快),因为软件开发者会编写出连大型主机也没法跟上的臃肿软件。

这在后来被证明是正确的——在Electron.js被发明出来后

1972年

Dennis Ritchie在贝尔实验室上班上到无聊了,于是他决定写出带有花括号的C语言,这门语言取得了巨大成功。随后他又增加了分段错误等对开发者友好的功能来辅助提高生产率。

折腾完这门语言之后他还有时间,于是他跟在贝尔实验室的伙计决定给C语言做个演示程序,结果他们弄出了一个操作系统,UNIX。

1980年

Alan Kay发明了一门面向对象语言,他把这门语言叫做Smalltalk,在Smalltalk中一切都是对象,甚至一个对象也是对象。没人真正搞得清楚small talk是什么意思。

1983年

Jean Ichbiah注意到Ada Lovelace的程序从来都没有实际运行过,决定以她的名字开发一门语言,但是这门语言还是没有跑起来。

1983年

Bjarne Stroustrup 注意到C在编译方面花的时间还不够多,于是他把自己能想到的每一项功能都增加了进去,然后称之为C++。

每一个地方的程序员都接受了它,因为这样他们在工作的时候找借口看阿猫阿狗视频和xkcd漫画就显得比较有诚意了。

1986年

Brac Box 和 Tol Move决定在Smalltalk的基础上制作一个C语言的不可读版本,他们把这门语言叫做Objective-C,但是没人弄得清楚它的语法。

1987年

Larry Wall有宗教经验,他成为了一名牧师,并且把Perl变成了一种教义。

1991年

Guido van Rossum不喜欢花括号,于是他发明了Python,语法选择的灵感来源自Monty Python(巨蟒剧团)和Flying Circus(飞行马戏团)。

1993年

Roberto Ierusalimschy和他的朋友认为自己需要一个巴西本地化的脚本语言,在本地化期间发生了一个错误,这个错误会把指针从1而不是0开始计算,他们把这门语言叫做Lua。

1994年

Rasmus Lerdorf给他个人主页的CGI脚本做了一个模板引擎,后来他把自己的资料都放到了网上。

世界决定将这些东西用到一切,Rasmus于是匆忙地将一些数据库绑定做了进去,并把这门语言叫做PHP。

1995年

Yukihiro Matsumoto不是很高兴,因为他注意到其他程序员不是很高兴。他创建了Ruby来让程序员高兴。在他创建了Ruby后“Matz”高兴了,Ruby社区高兴了,每个人都高兴了。

1995年

Brendan Eich利用周末时间设计了一门语言,打算用这门语言来为全世界的每一个主流浏览器乃至于最终的Skynet都提供动力。

他先是找到了Netscape然后说这门语言叫做LiveScript,但在代码评审期间Java变得流行起来,所以他们决定最好还是用花括号,然后就把它更名为JavaScript。

结果表明,Java却是一个会让他们惹上麻烦的商标,JavaScript随后更名为ECMAScript,但大家还是把它叫做JavaScript。

1996年

James Gosling发明了Java,第一们真正过于繁琐的面向对象语言,在这里设计模式完全压倒了实用主义。

于是就诞生了超级有效的管理器提供商、容器提供商、服务提供商、单一管理器提供商模式。

2001年

Anders Hejlsberg重新发明了Java然后把它叫做C#,因为用C来编程感觉要比Java酷。每个人都喜欢这个新版本的Java,因为它完全不像Java。

2005年

David Hanselmeyer Hansen创建了一个web框架叫做Ruby on Rails,从此大家不再记得Ruby和Rails是两个独立的东西了。

2006年

John Resig为JavaScript写了一个帮助库,每个人都以为那是一门语言,从此从互联网上拷贝粘贴jQuery代码就成为了一门职业。

2009年

Ken Thompson 和 Rob Pike 决定做一门类似C那样的语言,但要有更安全的装置,还要有更好的卖相,并且把Gopher(囊鼠)作为吉祥物。

他们把这门语言成为Go,并把它做成开源然后另外卖Gopher商标的护膝和头盔作为收入来源。

2010年

Graydon Hoare也想把语言做成C那样,他称之为Rust。每个人都要求马上用Rust把软件的每一块都重写一遍。Graydon希望做点更有亮点的事情,于是开始为苹果开发Swift。

2012年

Anders Hjelsberg希望在web浏览器里面写C#,于是他设计出TypeScript,这东西其实是JavaScript,但里面有了更多的Java的东西。

2013年

Jeremy Ashkenas想要像Ruby开发者一样快乐,于是他创建了CoffeeScript,这东西编译后像JavaScript但是样子又更像Ruby。Jerry从来都没有变得像Matz和Ruby开发者那样真正快乐。

2014年

Chris Lattner做Swift的时候,其主要的设计目标就是不要成为Objective-C,最后它看起来像Java。

原文链接:https://medium.com/@caspervonb/a-brief-totally-accurate-history-of-programming-languages-cd93ec806124

译者:36Kr 编译组   编辑:郝鹏程。

来源:编程语言简史:有人讨厌花括号,于是他发明了Python

https://www.oschina.net/news/92787/a-brief-totally-accurate-history-of-programming-languages

J2EE框架 Spring

J2EE框架 Spring 推荐

J2EE框架 面向方面AOP/IoC Web框架
Apache
Java
跨平台

Spring Framework 是一个开源的Java/Java EE全功能栈(full-stack)的应用程序框架,以Apache许可证形式发布,也有.NET平台上的移植版本。该框架基于 Expert One-on-One Java EE Design and Development(ISBN 0-7645-4385-7)一书中的代码,最初由 Rod Johnson 和 Juergen Hoeller等开发。Spring Framework 提供了一个简易的开发方式,这种开发方式,将避免那些可能致使底层代码变得繁杂混乱的大量的属性文件和帮助类。

Spring 中包含的关键特性:

  • 强大的基于 JavaBeans 的采用控制翻转(Inversion of Control,IoC)原则的配置管理,使得应用程序的组建更加快捷简易。
  • 一个可用于从 applet 到 Java EE 等不同运行环境的核心 Bean 工厂。
  • 数据库事务的一般化抽象层,允许宣告式(Declarative)事务管理器,简化事务的划分使之与底层无关。
  • 内建的针对 JTA 和 单个 JDBC 数据源的一般化策略,使 Spring 的事务支持不要求 Java EE 环境,这与一般的 JTA 或者 EJB CMT 相反。
  • JDBC 抽象层提供了有针对性的异常等级(不再从SQL异常中提取原始代码), 简化了错误处理, 大大减少了程序员的编码量. 再次利用JDBC时,你无需再写出另一个 ‘终止’ (finally) 模块. 并且面向JDBC的异常与Spring 通用数据访问对象 (Data Access Object) 异常等级相一致.
  • 以资源容器,DAO 实现和事务策略等形式与 Hibernate,JDO 和 iBATIS SQL Maps 集成。利用众多的翻转控制方便特性来全面支持, 解决了许多典型的Hibernate集成问题. 所有这些全部遵从Spring通用事务处理和通用数据访问对象异常等级规范.
  • 灵活的基于核心 Spring 功能的 MVC 网页应用程序框架。开发者通过策略接口将拥有对该框架的高度控制,因而该框架将适应于多种呈现(View)技术,例如 JSP,FreeMarker,Velocity,Tiles,iText 以及 POI。值得注意的是,Spring 中间层可以轻易地结合于任何基于 MVC 框架的网页层,例如 Struts,WebWork,或 Tapestry。
  • 提供诸如事务管理等服务的面向方面编程框架。

在设计应用程序Model时,MVC 模式(例如Struts)通常难于给出一个简洁明了的框架结构。Spring却具有能够让这部分工作变得简单的能力。程序开发员们可以使用Spring的 JDBC 抽象层重新设计那些复杂的框架结构。

React 定义组件的参数-生命周期

定义组件的参数-生命周期
创建期:getDefaultProps
创建期:getInitialState
创建期:componentWillMount
创建期:componentDidMount
存在期:componentWillReceiveProps
存在期:shouldComponentUpdate
存在期:componentWillUpdate
存在期:componentDidUpdate
销毁&清理期:componentWillUnmount

定义组件的参数-生命周期
生命周期相关参数,是React定义组件时提供的一系列处理函数(钓子函数),这些函数会在组件生命周期的某个阶段调用。

创建期:getDefaultProps

object getDefaultProps()

创建期:getInitialState

object getInitialState()
在组件挂载前(即:创建期)调用一次,其返回值将做为this.state的初始值。

getInitialState()方法会组件类创建的时候调用一次,其返回值会被缓存下来。该方法用于设置props属性的默认值,但仅对于非必须属性。如果父组件没有指定props中的某个值,此返回对象中的相应属性将会合并到this.props。

getInitialState()方法会在组件实例创建前调用,这时还不能使用this.props属性,且其返回对象是在所有实例间共享的。

创建期:componentWillMount

componentWillMount()
componentWillMount()服务器端和客户端都只调用一次,在初始化渲染执行之前被调用。如果在这个方法内调用setState()方法,render()方法将会收到更新后的state,也就是说这是我做在组件渲染前最后一个修改state的机会。

创建期:componentDidMount

componentDidMount()
componentDidMount()会在组件初始化(渲染完成)后立即调用一次,我们一般在这个方法中使用this.getDOMNode()方法访问原始DOM。

存在期:componentWillReceiveProps

componentWillReceiveProps(object nextProps)

componentWillReceiveProps在将要接受新的props时被调用
componentWillReceiveProps()方法会在组件生命周期的存在期调用,当组件感知到props属性改变,会调用此方法。render()方法将会在其后调用,这时我们可以通过this.setState()来阻止组件的再次渲染。

存在期:shouldComponentUpdate

boolean shouldComponentUpdate(object nextProps, object nextState)
shouldComponentUpdate()方法发生在组件生命周期的存在器,在组件收到新的props或state。在这个方法中,我们可以访问组件的props和state属性,通过这两个属性可以确认组件是否需要更新,如果不需要更新,则返回false,则其后的方法将不会在执行。如:

shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.id !== this.props.id;
}

存在期:componentWillUpdate

componentWillUpdate(object nextProps, object nextState)
componentWillUpdate()会在收到新的props或state后调用,类似componentWillMount()。

存在期:componentDidUpdate

componentDidUpdate(object prevProps, object prevState)
componentDidUpdate()会在组件重新渲染后立即被调用,当我们需要在组件重新渲染后操作DOM则需要使用这个方法。

销毁&清理期:componentWillUnmount

componentWillUnmount()
componentWillUnmount()是组件销毁&清理期唯一调用的方法,它会在组件从DOM中移除时被调用,这时我们可以清理一些状态或清理在componentDidMount中创建的DOM元素。

Hibernate5 注解 命名策略

对于hibernate注解实体中属性对应数据库表的列名,怎么命名的问题,我们肯定不愿一个个属性去配置

Hibernaet5.1 之前  在applicationContext.xml中的sessionFactory中配置

<property name=”namingStrategy”>
<bean class=”org.hibernate.cfg.ImprovedNamingStrategy”></bean>
</property>

5.1开始

hibernate.ejb.naming_strategy将不再被支持,而是被替换成了两个属性:
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy

对于physical_naming_strategy有两个常用的配置:

org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

对于PhysicalNamingStrategyStandardImplDefaultNamingStrategy的效果,

对于SpringPhysicalNamingStrategyImprovedNamingStrategy的效果。

法1:在sessionFactory的bean里配置

<property name=”PhysicalNamingStrategy”>
<bean class=”org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl”></bean>
</property>

法2:在sessionFactory bean的hibernateProperties property中配置

<prop key=”hibernate.physical_naming_strategy”>org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl</prop>

org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy 是 spring boot 包提供地

<property name=”ImplicitNamingStrategy”>
<bean class=”org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl” />
</property>

或是

<prop key=”hibernate.implicit_naming_strategy”>org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl</prop>

基于Dubbo框架构建分布式服务 集群容错

Dubbo是Alibaba开源的分布式服务框架,我们可以非常容易地通过Dubbo来构建分布式服务,并根据自己实际业务应用场景来选择合适的集群容错模式,这个对于很多应用都是迫切希望的,只需要通过简单的配置就能够实现分布式服务调用,也就是说服务提供方(Provider)发布的服务可以天然就是集群服务,比如,在实时性要求很高的应用场景下,可能希望来自消费方(Consumer)的调用响应时间最短,只需要选择Dubbo的Forking Cluster模式配置,就可以对一个调用请求并行发送到多台对等的提供方(Provider)服务所在的节点上,只选择最快一个返回响应的,然后将调用结果返回给服务消费方(Consumer),显然这种方式是以冗余服务为基础的,需要消耗更多的资源,但是能够满足高实时应用的需求。
有关Dubbo服务框架的简单使用,可以参考我的其他两篇文章(《基于Dubbo的Hessian协议实现远程调用》,《Dubbo实现RPC调用使用入门》,后面参考链接中已给出链接),这里主要围绕Dubbo分布式服务相关配置的使用来说明与实践。

Dubbo服务集群容错

假设我们使用的是单机模式的Dubbo服务,如果在服务提供方(Provider)发布服务以后,服务消费方(Consumer)发出一次调用请求,恰好这次由于网络问题调用失败,那么我们可以配置服务消费方重试策略,可能消费方第二次重试调用是成功的(重试策略只需要配置即可,重试过程是透明的);但是,如果服务提供方发布服务所在的节点发生故障,那么消费方再怎么重试调用都是失败的,所以我们需要采用集群容错模式,这样如果单个服务节点因故障无法提供服务,还可以根据配置的集群容错模式,调用其他可用的服务节点,这就提高了服务的可用性。
首先,根据Dubbo文档,我们引用文档提供的一个架构图以及各组件关系说明,如下所示:

上述各个组件之间的关系(引自Dubbo文档)说明如下:

  • 这里的Invoker是Provider的一个可调用Service的抽象,Invoker封装了Provider地址及Service接口信息。
  • Directory代表多个Invoker,可以把它看成List,但与List不同的是,它的值可能是动态变化的,比如注册中心推送变更。
  • Cluster将Directory中的多个Invoker伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。
  • Router负责从多个Invoker中按路由规则选出子集,比如读写分离,应用隔离等。
  • LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。

我们也简单说明目前Dubbo支持的集群容错模式,每种模式适应特定的应用场景,可以根据实际需要进行选择。Dubbo内置支持如下6种集群模式:

  • Failover Cluster模式

配置值为failover。这种模式是Dubbo集群容错默认的模式选择,调用失败时,会自动切换,重新尝试调用其他节点上可用的服务。对于一些幂等性操作可以使用该模式,如读操作,因为每次调用的副作用是相同的,所以可以选择自动切换并重试调用,对调用者完全透明。可以看到,如果重试调用必然会带来响应端的延迟,如果出现大量的重试调用,可能说明我们的服务提供方发布的服务有问题,如网络延迟严重、硬件设备需要升级、程序算法非常耗时,等等,这就需要仔细检测排查了。
例如,可以这样显式指定Failover模式,或者不配置则默认开启Failover模式,配置示例如下:

1 <dubbo:service interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService"version="1.0.0"
2      cluster="failover" retries="2" timeout="100"ref="chatRoomOnlineUserCounterService" protocol="dubbo" >
3      <dubbo:method name="queryRoomUserCount" timeout="80" retries="2" />
4 </dubbo:service>

上述配置使用Failover Cluster模式,如果调用失败一次,可以再次重试2次调用,服务级别调用超时时间为100ms,调用方法queryRoomUserCount的超时时间为80ms,允许重试2次,最坏情况调用花费时间160ms。如果该服务接口org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService还有其他的方法可供调用,则其他方法没有显式配置则会继承使用dubbo:service配置的属性值。

  • Failfast Cluster模式

配置值为failfast。这种模式称为快速失败模式,调用只执行一次,失败则立即报错。这种模式适用于非幂等性操作,每次调用的副作用是不同的,如写操作,比如交易系统我们要下订单,如果一次失败就应该让它失败,通常由服务消费方控制是否重新发起下订单操作请求(另一个新的订单)。

  • Failsafe Cluster模式

配置值为failsafe。失败安全模式,如果调用失败, 则直接忽略失败的调用,而是要记录下失败的调用到日志文件,以便后续审计。

  • Failback Cluster模式

配置值为failback。失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。

  • Forking Cluster模式

配置值为forking。并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。

  • Broadcast Cluster模式

配置值为broadcast。广播调用所有提供者,逐个调用,任意一台报错则报错(2.1.0开始支持)。通常用于通知所有提供者更新缓存或日志等本地资源信息。
上面的6种模式都可以应用于生产环境,我们可以根据实际应用场景选择合适的集群容错模式。如果我们觉得Dubbo内置提供的几种集群容错模式都不能满足应用需要,也可以定制实现自己的集群容错模式,因为Dubbo框架给我提供的扩展的接口,只需要实现接口com.alibaba.dubbo.rpc.cluster.Cluster即可,接口定义如下所示:

01 @SPI(FailoverCluster.NAME)
02 public interface Cluster {
03
04     /**
05      * Merge the directory invokers to a virtual invoker.
06      * @param <T>
07      * @param directory
08      * @return cluster invoker
09      * @throws RpcException
10      */
11     @Adaptive
12     <T> Invoker<T> join(Directory<T> directory) throws RpcException;
13
14 }

关于如何实现一个自定义的集群容错模式,可以参考Dubbo源码中内置支持的汲取你容错模式的实现,6种模式对应的实现类如下所示:

1 com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
2 com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
3 com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
4 com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
5 com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
6 com.alibaba.dubbo.rpc.cluster.support.AvailableCluster

可能我们初次接触Dubbo时,不知道如何在实际开发过程中使用Dubbo的集群模式,后面我们会以Failover Cluster模式为例开发我们的分布式应用,再进行详细的介绍。

Dubbo服务负载均衡

Dubbo框架内置提供负载均衡的功能以及扩展接口,我们可以透明地扩展一个服务或服务集群,根据需要非常容易地增加/移除节点,提高服务的可伸缩性。Dubbo框架内置提供了4种负载均衡策略,如下所示:

  • Random LoadBalance:随机策略,配置值为random。可以设置权重,有利于充分利用服务器的资源,高配的可以设置权重大一些,低配的可以稍微小一些
  • RoundRobin LoadBalance:轮询策略,配置值为roundrobin。
  • LeastActive LoadBalance:配置值为leastactive。根据请求调用的次数计数,处理请求更慢的节点会受到更少的请求
  • ConsistentHash LoadBalance:一致性Hash策略,具体配置方法可以参考Dubbo文档。相同调用参数的请求会发送到同一个服务提供方节点上,如果某个节点发生故障无法提供服务,则会基于一致性Hash算法映射到虚拟节点上(其他服务提供方)

在实际使用中,只需要选择合适的负载均衡策略值,配置即可,下面是上述四种负载均衡策略配置的示例:

1 <dubbo:service interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService"version="1.0.0"
2      cluster="failover" retries="2" timeout="100" loadbalance="random"
3      ref="chatRoomOnlineUserCounterService" protocol="dubbo" >
4      <dubbo:method name="queryRoomUserCount" timeout="80" retries="2"loadbalance="leastactive" />
5 </dubbo:service>

上述配置,也体现了Dubbo配置的继承性特点,也就是dubbo:service元素配置了loadbalance=”random”,则该元素的子元素dubbo:method如果没有指定负载均衡策略,则默认为loadbalance=”random”,否则如果dubbo:method指定了loadbalance=”leastactive”,则使用子元素配置的负载均衡策略覆盖了父元素指定的策略(这里调用queryRoomUserCount方法使用leastactive负载均衡策略)。
当然,Dubbo框架也提供了实现自定义负载均衡策略的接口,可以实现com.alibaba.dubbo.rpc.cluster.LoadBalance接口,接口定义如下所示:

01 /**
02 * LoadBalance. (SPI, Singleton, ThreadSafe)
03 *
04 * <a href="http://en.wikipedia.org/wiki/Load_balancing_(computing)">Load-Balancing</a>
05 *
06 * @see com.alibaba.dubbo.rpc.cluster.Cluster#join(Directory)
07 * @author qian.lei
08 * @author william.liangf
09 */
10 @SPI(RandomLoadBalance.NAME)
11 public interface LoadBalance {
12
13      /**
14      * select one invoker in list.
15      * @param invokers invokers.
16      * @param url refer url
17      * @param invocation invocation.
18      * @return selected invoker.
19      */
20     @Adaptive("loadbalance")
21      <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
22
23 }

如何实现一个自定义负载均衡策略,可以参考Dubbo框架内置的实现,如下所示的3个实现类:

1 com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
2 com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
3 com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance

Dubbo服务集群容错实践

手机应用是以聊天室为基础的,我们需要收集用户的操作行为,然后计算聊天室中在线人数,并实时在手机应用端显示人数,整个系统的架构如图所示:

上图中,主要包括了两大主要流程:日志收集并实时处理流程、调用读取实时计算结果流程,我们使用基于Dubbo框架开发的服务来提供实时计算结果读取聊天人数的功能。上图中,实际上业务接口服务器集群也可以基于Dubbo框架构建服务,就看我们想要构建什么样的系统来满足我们的需要。
如果不使用注册中心,服务消费方也能够直接调用服务提供方发布的服务,这样需要服务提供方将服务地址暴露给服务消费方,而且也无法使用监控中心的功能,这种方式成为直连。
如果我们使用注册中心,服务提供方将服务发布到注册中心,而服务消费方可以通过注册中心订阅服务,接收服务提供方服务变更通知,这种方式可以隐藏服务提供方的细节,包括服务器地址等敏感信息,而服务消费方只能通过注册中心来获取到已注册的提供方服务,而不能直接跨过注册中心与服务提供方直接连接。这种方式的好处是还可以使用监控中心服务,能够对服务的调用情况进行监控分析,还能使用Dubbo服务管理中心,方便管理服务,我们在这里使用的是这种方式,也推荐使用这种方式。使用注册中心的Dubbo分布式服务相关组件结构,如下图所示:

下面,开发部署我们的应用,通过如下4个步骤来完成:

  • 服务接口定义

服务接口将服务提供方(Provider)和服务消费方(Consumer)连接起来,服务提供方实现接口中定义的服务,即给出服务的实现,而服务消费方负责调用服务。我们接口中给出了2个方法,一个是实时查询获取当前聊天室内人数,另一个是查询一天中某个/某些聊天室中在线人数峰值,接口定义如下所示:

01 package org.shirdrn.dubbo.api;
02
03 import java.util.List;
04
05 public interface ChatRoomOnlineUserCounterService {
06
07      String queryRoomUserCount(String rooms);
08     
09      List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat);
10 }

接口是服务提供方和服务消费方公共遵守的协议,一般情况下是服务提供方将接口定义好后提供给服务消费方。

  • 服务提供方

服务提供方实现接口中定义的服务,其实现和普通的服务没什么区别,我们的实现类为ChatRoomOnlineUserCounterServiceImpl,代码如下所示:

01 package org.shirdrn.dubbo.provider.service;
02
03 import java.util.List;
04
05 import org.apache.commons.logging.Log;
06 import org.apache.commons.logging.LogFactory;
07 import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService;
08 import org.shirdrn.dubbo.common.utils.DateTimeUtils;
09
10 import redis.clients.jedis.Jedis;
11 import redis.clients.jedis.JedisPool;
12
13 import com.alibaba.dubbo.common.utils.StringUtils;
14 import com.google.common.base.Strings;
15 import com.google.common.collect.Lists;
16
17 public class ChatRoomOnlineUserCounterServiceImpl implementsChatRoomOnlineUserCounterService {
18
19      private static final Log LOG = LogFactory.getLog(ChatRoomOnlineUserCounterServiceImpl.class);
20      private JedisPool jedisPool;
21      private static final String KEY_USER_COUNT = "chat::room::play::user::cnt";
22      private static final String KEY_MAX_USER_COUNT_PREFIX = "chat::room::max::user::cnt::";
23      private static final String DF_YYYYMMDD = "yyyyMMdd";
24
25      public String queryRoomUserCount(String rooms) {
26           LOG.info("Params[Server|Recv|REQ] rooms=" + rooms);
27           StringBuffer builder = new StringBuffer();
28           if(!Strings.isNullOrEmpty(rooms)) {
29                Jedis jedis = null;
30                try {
31                     jedis = jedisPool.getResource();
32                     String[] fields = rooms.split(",");
33                     List<String> results = jedis.hmget(KEY_USER_COUNT, fields);
34                     builder.append(StringUtils.join(results, ","));
35                } catch (Exception e) {
36                     LOG.error("", e);
37                } finally {
38                     if(jedis != null) {
39                          jedis.close();
40                     }
41                }
42           }
43           LOG.info("Result[Server|Recv|RES] " + builder.toString());
44           return builder.toString();
45      }
46     
47      @Override
48      public List<String> getMaxOnlineUserCount(List<String> rooms, String date, String dateFormat) {
49           // HGETALL chat::room::max::user::cnt::20150326
50           LOG.info("Params[Server|Recv|REQ] rooms=" + rooms + ",date=" + date + ",dateFormat=" + dateFormat);
51           String whichDate = DateTimeUtils.format(date, dateFormat, DF_YYYYMMDD);
52           String key = KEY_MAX_USER_COUNT_PREFIX + whichDate;
53           StringBuffer builder = new StringBuffer();
54           if(rooms != null && !rooms.isEmpty()) {
55                Jedis jedis = null;
56                try {
57                     jedis = jedisPool.getResource();
58                     return jedis.hmget(key, rooms.toArray(new String[rooms.size()]));
59                } catch (Exception e) {
60                     LOG.error("", e);
61                } finally {
62                     if(jedis != null) {
63                          jedis.close();
64                     }
65                }
66           }
67           LOG.info("Result[Server|Recv|RES] " + builder.toString());
68           return Lists.newArrayList();
69      }
70     
71      public void setJedisPool(JedisPool jedisPool) {
72           this.jedisPool = jedisPool;
73      }
74
75 }

代码中通过读取Redis中数据来完成调用,逻辑比较简单。对应的Maven POM依赖配置,如下所示:

01 <dependencies>
02      <dependency>
03           <groupId>org.shirdrn.dubbo</groupId>
04           <artifactId>dubbo-api</artifactId>
05           <version>0.0.1-SNAPSHOT</version>
06      </dependency>
07      <dependency>
08           <groupId>org.shirdrn.dubbo</groupId>
09           <artifactId>dubbo-commons</artifactId>
10           <version>0.0.1-SNAPSHOT</version>
11      </dependency>
12      <dependency>
13           <groupId>redis.clients</groupId>
14           <artifactId>jedis</artifactId>
15           <version>2.5.2</version>
16      </dependency>
17      <dependency>
18           <groupId>org.apache.commons</groupId>
19           <artifactId>commons-pool2</artifactId>
20           <version>2.2</version>
21      </dependency>
22      <dependency>
23           <groupId>org.jboss.netty</groupId>
24           <artifactId>netty</artifactId>
25           <version>3.2.7.Final</version>
26      </dependency>
27 </dependencies>

有关对Dubbo框架的一些依赖,我们单独放到一个通用的Maven Module中(详见后面“附录:Dubbo使用Maven构建依赖配置”),这里不再多说。服务提供方实现,最关键的就是服务的配置,因为Dubbo基于Spring来管理配置和实例,所以通过配置可以指定服务是否是分布式服务,以及通过配置增加很多其它特性。我们的配置文件为provider-cluster.xml,内容如下所示:

01 <?xml version="1.0" encoding="UTF-8"?>
02
03 <beans xmlns="http://www.springframework.org/schema/beans"
04      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
05      xmlns:p="http://www.springframework.org/schema/p"
06      xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd
07      http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
08
09      <beanclass="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
10           <property name="systemPropertiesModeName"value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
11           <property name="ignoreResourceNotFound" value="true" />
12           <property name="locations">
13                <list>
14                     <value>classpath*:jedis.properties</value>
15                </list>
16           </property>
17      </bean>
18     
19      <dubbo:application name="chatroom-cluster-provider" />
20      <dubbo:registry address="zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" />
21     
22      <dubbo:protocol name="dubbo" port="20880" />
23     
24      <dubbo:service interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService"version="1.0.0"
25           cluster="failover" retries="2" timeout="1000" loadbalance="random"actives="100" executes="200"
26           ref="chatRoomOnlineUserCounterService" protocol="dubbo" >
27           <dubbo:method name="queryRoomUserCount" timeout="500" retries="2"loadbalance="roundrobin" actives="50" />
28      </dubbo:service>
29     
30      <bean id="chatRoomOnlineUserCounterService"class="org.shirdrn.dubbo.provider.service.ChatRoomOnlineUserCounterServiceImpl" >
31           <property name="jedisPool" ref="jedisPool" />
32      </bean>
33     
34      <bean id="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="destroy">
35           <constructor-arg index="0">
36                <bean class="org.apache.commons.pool2.impl.GenericObjectPoolConfig">
37                     <property name="maxTotal" value="${redis.pool.maxTotal}" />
38                     <property name="maxIdle" value="${redis.pool.maxIdle}" />
39                     <property name="minIdle" value="${redis.pool.minIdle}" />
40                     <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}"/>
41                     <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
42                     <property name="testOnReturn" value="${redis.pool.testOnReturn}" />
43                     <property name="testWhileIdle" value="true" />
44                </bean>
45           </constructor-arg>
46           <constructor-arg index="1" value="${redis.host}" />
47           <constructor-arg index="2" value="${redis.port}" />
48           <constructor-arg index="3" value="${redis.timeout}" />
49      </bean>
50     
51 </beans>

上面配置中,使用dubbo协议,集群容错模式为failover,服务级别负载均衡策略为random,方法级别负载均衡策略为roundrobin(它覆盖了服务级别的配置内容),其他一些配置内容可以参考Dubbo文档。我们这里是从Redis读取数据,所以使用了Redis连接池。
启动服务示例代码如下所示:

01 package org.shirdrn.dubbo.provider;
02
03 import org.shirdrn.dubbo.provider.common.DubboServer;
04
05 public class ChatRoomClusterServer {
06
07      public static void main(String[] args) throws Exception {
08           DubboServer.startServer("classpath:provider-cluster.xml");
09      }
10
11 }

上面调用了DubboServer类的静态方法startServer,如下所示:

01 public static void startServer(String config) {
02      ClassPathXmlApplicationContext context = newClassPathXmlApplicationContext(config);
03      try {
04           context.start();
05           System.in.read();
06      } catch (IOException e) {
07           e.printStackTrace();
08      } finally {
09           context.close();
10      }
11 }

方法中主要是初始化Spring IoC容器,全部对象都交由容器来管理。

  • 服务消费方

服务消费方就容易了,只需要知道注册中心地址,并引用服务提供方提供的接口,消费方调用服务实现如下所示:

01 package org.shirdrn.dubbo.consumer;
02
03 import java.util.Arrays;
04 import java.util.List;
05
06 import org.apache.commons.logging.Log;
07 import org.apache.commons.logging.LogFactory;
08 import org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService;
09 import org.springframework.context.support.AbstractXmlApplicationContext;
10 import org.springframework.context.support.ClassPathXmlApplicationContext;
11
12 public class ChatRoomDubboConsumer {
13
14      private static final Log LOG = LogFactory.getLog(ChatRoomDubboConsumer.class);
15     
16      public static void main(String[] args) throws Exception {
17           AbstractXmlApplicationContext context = newClassPathXmlApplicationContext("classpath:consumer.xml");
18           try {
19                context.start();
20                ChatRoomOnlineUserCounterService chatRoomOnlineUserCounterService = (ChatRoomOnlineUserCounterService) context.getBean("chatRoomOnlineUserCounterService");        
21                getMaxOnlineUserCount(chatRoomOnlineUserCounterService);             
22                getRealtimeOnlineUserCount(chatRoomOnlineUserCounterService);             
23                System.in.read();
24           } finally {
25                context.close();
26           }
27          
28      }
29
30      private static void getMaxOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService) {
31           List<String> maxUserCounts = liveRoomOnlineUserCountService.getMaxOnlineUserCount(
32                     Arrays.asList(new String[] {"1482178010" , "1408492761", "1430546839", "1412517075", "1435861734"}), "20150327", "yyyyMMdd");
33           LOG.info("After getMaxOnlineUserCount invoked: maxUserCounts= " + maxUserCounts);
34      }
35
36      private static void getRealtimeOnlineUserCount(ChatRoomOnlineUserCounterService liveRoomOnlineUserCountService)
37                throws InterruptedException {
38           String rooms = "1482178010,1408492761,1430546839,1412517075,1435861734";
39           String onlineUserCounts = liveRoomOnlineUserCountService.queryRoomUserCount(rooms);
40           LOG.info("After queryRoomUserCount invoked: onlineUserCounts= " + onlineUserCounts);
41      }
42 }

对应的配置文件为consumer.xml,内容如下所示:

01 <?xml version="1.0" encoding="UTF-8"?>
02
03 <beans xmlns="http://www.springframework.org/schema/beans"
04      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
05      xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd
06      http://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd">
07
08      <dubbo:application name="chatroom-consumer" />
09      <dubbo:registry address="zookeeper://zk1:2181?backup=zk2:2181,zk3:2181" />
10     
11      <dubbo:reference id="chatRoomOnlineUserCounterService"interface="org.shirdrn.dubbo.api.ChatRoomOnlineUserCounterService" version="1.0.0">
12           <dubbo:method name="queryRoomUserCount" retries="2" />
13      </dubbo:reference>
14
15 </beans>

也可以根据需要配置dubbo:reference相关的属性值,也可以配置dubbo:method指定调用的方法的配置信息,详细配置属性可以参考Dubbo官方文档。

  • 部署与验证

开发完成提供方服务后,在本地开发调试的时候可以怎么简单怎么做,如果是要部署到生产环境,则需要打包后进行部署,可以参考下面的Maven POM配置:

01 <build>
02      <plugins>
03           <plugin>
04                <groupId>org.apache.maven.plugins</groupId>
05                <artifactId>maven-shade-plugin</artifactId>
06                <version>1.4</version>
07                <configuration>
08                     <createDependencyReducedPom>true</createDependencyReducedPom>
09                </configuration>
10                <executions>
11                     <execution>
12                          <phase>package</phase>
13                          <goals>
14                               <goal>shade</goal>
15                          </goals>
16                          <configuration>
17                               <transformers>
18                                    <transformerimplementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
19                                    <transformerimplementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
20                                         <mainClass>org.shirdrn.dubbo.provider.ChatRoomClusterServer</mainClass>
21                                    </transformer>
22                               </transformers>
23                          </configuration>
24                     </execution>
25                </executions>
26           </plugin>
27      </plugins>
28 </build>

这里也给出Maven POM依赖的简单配置:

1 <dependencies>
2      <dependency>
3           <groupId>org.shirdrn.dubbo</groupId>
4           <artifactId>dubbo-api</artifactId>
5           <version>0.0.1-SNAPSHOT</version>
6      </dependency>
7 </dependencies>

我们开发的服务应该是分布式的,首先是通过配置内容来决定,例如设置集群模式、设置负载均衡模式等,然后在部署的时候,可以在多个节点上同一个服务,这样多个服务都会注册到Dubbo注册中心,如果某个节点上的服务不可用了,可以根据我们配置的策略来选择其他节点上的可用服务,后面通过Dubbo服务管理中心和监控中心就能更加清楚明了。

来源:http://shiyanjun.cn/archives/1075.html

struts2 upgrade 2.3 to 2.5 migration 升级向导

Dependencies

Update Struts dependencies to 2.5.

Remove the following plugin dependencies because they were dropped and aren’t supported anymore.

  • Dojo Plugin
  • Codebehind Plugin
  • JSF Plugin
  • Struts1 Plugin

StrutsPrepareAndExecuteFilter

The org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter was moved to org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter.

In web.xml replace this:

<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>

with that:

<filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>

There were other package changes, please read Version Notes 2.5 for more details.

DTD

Struts DTD was updated to 2.5 version.

In struts.xml replace 2.3 DTD version:

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"

with 2.5:

<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN"

Tags attributes

The id attribute was replaced with var attribute in the following tags.

  • <s:action>
  • <s:append>
  • <s:bean>
  • <s:date>
  • <s:generator>
  • <s:iterator>
  • <s:merge>
  • <s:number>
  • <s:set>
  • <s:sort>
  • <s:subset>
  • <s:text>
  • <s:url>

If you have something like that in your code:

<s:url id="url" action="login">

change it to:

<s:url var="url" action="login">

The <s:set> tag name attribute is replaced with var attribute.

From:

<s:set id="str1" value="'string1 value'" />
<s:set name="str2" value="'string2 value'" />

to:

<s:set var="str1" value="'string1 value'" />
<s:set var="str2" value="'string2 value'" />

Also escape attribute was renamed to escapeHtml attribute.

From:

<s:property escape="true" var="someProperty"/>

to:

<s:property escapeHtml="true" var="someProperty"/>

Div tag

The <s:div> tag was dropped.

Replace <s:div> with plain HTML <div> tag.

Field names

If you have field names which starts with single lower case letter, for example:

private String sTrng;
public String getSTrng() {...}
public void setSTrng(String str) {...}

change accessors to getsTrng and setsTrng.

Or better yet, change field names to not contain single lower case letter:

private String strng;
public String getStrng() {...}
public void setStrng(String str) {...}

For additional info see WW-3909.

Tiles

Depending on from which version of struts you upgrade and whether you used tiles-plugin or tiles3-plugin you may need to do different steps.

Struts 2.5 just provides a tiles-plugin which uses Tiles3. So support for Tiles2 has been dropped as well as the name tiles3-plugin.

Now the only maven dependency looks like this:

maven dependecy for tiles-plugin
<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-tiles-plugin</artifactId>
    <version>${struts2.version}</version>
</dependency>

You may need to update DTD in your tiles.xml files to Tiles3:

tiles3 dtd
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"

A Listener in web.xml is required. It is not necessary to configure paths to tiles.xml files here as they are picked up automatically.

StrutsTilesListener in web.xml
<listener>
  <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
</listener>

Optionally you may remove TilesDefinitions from XML and annotate actions instead. See Tiles Plugin for more details.

Temp/Work directory of ApplicationServer/ServletContainer

Users reported it was necessary for them to remove temp/work directory of their ApplicationServer/ServletContainer. Likely to force server to recompile JSPs.

来源:https://cwiki.apache.org/confluence/display/WW/Struts%202.3%20to%202.5%20migration

Java的基本数据类型

变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。

内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。

因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。

Java的两大数据类型:

  • 内置数据类型
  • 引用数据类型

内置数据类型

Java语言提供了八种基本类型。六种数字类型(四个整数型(默认是int 型),两个浮点型(默认是double 型)),一种字符类型,还有一种布尔型。

byte:

  • byte数据类型是8位、有符号的,以二进制补码表示的整数;(256个数字),占1字节
  • 最小值是-128(-2^7);
  • 最大值是127(2^7-1);
  • 默认值是0;
  • byte类型用在大型数组中节约空间,主要代替整数,因为byte变量占用的空间只有int类型的四分之一;
  • 例子:byte a = 100,byte b = -50。

short:

  • short数据类型是16位、有符号的以二进制补码表示的整数,占2字节
  • 最小值是-32768(-2^15);
  • 最大值是32767(2^15 – 1);
  • Short数据类型也可以像byte那样节省空间。一个short变量是int型变量所占空间的二分之一;
  • 默认值是0;

int:

  • int数据类型是32位、有符号的以二进制补码表示的整数;占3字节
  • 最小值是-2,147,483,648(-2^31);
  • 最大值是2,147,485,647(2^31 – 1);
  • 一般地整型变量默认为int类型;
  • 默认值是0;
  • 例子:int a = 100000, int b = -200000。

long:

  • long数据类型是64位、有符号的以二进制补码表示的整数;占4字节
  • 最小值是-9,223,372,036,854,775,808(-2^63);
  • 最大值是9,223,372,036,854,775,807(2^63 -1);
  • 这种类型主要使用在需要比较大整数的系统上;
  • 默认值是0L;
  • char数据类型可以储存任何字符;
  • 例子:char letter = ‘A’。

char 类型可以参与整型计算,然后转换成字符型

对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:

public class PrimitiveTypeTest {  
    public static void main(String[] args) {  
        // byte  
        System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);  
        System.out.println("包装类:java.lang.Byte");  
        System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);  
        System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);  
        System.out.println();  
  
        // short  
        System.out.println("基本类型:short 二进制位数:" + Short.SIZE);  
        System.out.println("包装类:java.lang.Short");  
        System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);  
        System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);  
        System.out.println();  
  
        // int  
        System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);  
        System.out.println("包装类:java.lang.Integer");  
        System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);  
        System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);  
        System.out.println();  
  
        // long  
        System.out.println("基本类型:long 二进制位数:" + Long.SIZE);  
        System.out.println("包装类:java.lang.Long");  
        System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);  
        System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);  
        System.out.println();  
  
        // float  
        System.out.println("基本类型:float 二进制位数:" + Float.SIZE);  
        System.out.println("包装类:java.lang.Float");  
        System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);  
        System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);  
        System.out.println();  
  
        // double  
        System.out.println("基本类型:double 二进制位数:" + Double.SIZE);  
        System.out.println("包装类:java.lang.Double");  
        System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);  
        System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);  
        System.out.println();  
  
        // char  
        System.out.println("基本类型:char 二进制位数:" + Character.SIZE);  
        System.out.println("包装类:java.lang.Character");  
        // 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台  
        System.out.println("最小值:Character.MIN_VALUE="  
                + (int) Character.MIN_VALUE);  
        // 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台  
        System.out.println("最大值:Character.MAX_VALUE="  
                + (int) Character.MAX_VALUE);  
    }  
}  

编译以上代码输出结果如下所示:

基本类型:byte 二进制位数:8
包装类:java.lang.Byte
最小值:Byte.MIN_VALUE=-128
最大值:Byte.MAX_VALUE=127

基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767

基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647

基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807

基本类型:float 二进制位数:32
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38

基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308

基本类型:char 二进制位数:16
包装类:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535

Float和Double的最小值和最大值都是以科学记数法的形式输出的,结尾的”E+数字”表示E之前的数字要乘以10的多少倍。比如3.14E3就是3.14×1000=3140,3.14E-3就是3.14/1000=0.00314。

实际上,JAVA中还存在另外一种基本类型void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。


引用类型

  • 引用类型变量由类的构造函数创建,可以使用它们访问所引用的对象。这些变量在声明时被指定为一个特定的类型,比如Employee、Pubby等。变量一旦声明后,类型就不能被改变了。
  • 对象、数组都是引用数据类型。
  • 所有引用类型的默认值都是null。
  • 一个引用变量可以用来引用与任何与之兼容的类型。
  • 例子:Animal animal = new Animal(“giraffe”)。

Java常量

常量就是一个固定值。它们不需要计算,直接代表相应的值。

常量指不能改变的量。 在Java中用final标志,声明方式和变量类似:

final double PI = 3.1415927;

虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。

字面量可以赋给任何内置类型的变量。例如:

byte a = 68;
char a = 'A'

byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。

当使用常量的时候,前缀0表示8进制,而前缀0x代表16进制。例如:

int decimal = 100;
int octal = 0144;
int hexa =  0x64;

和其他语言一样,Java的字符串常量也是包含在两个引号之间的字符序列。下面是字符串型字面量的例子:

"Hello World"
"two\nlines"
"\"This is in quotes\""

字符串常量和字符常量都可以包含任何Unicode字符。例如:

char a = '\u0001';
String a = "\u0001";

Java语言支持一些特殊的转义字符序列。

符号 字符含义
\n 换行 (0x0a)
\r 回车 (0x0d)
\f 换页符(0x0c)
\b 退格 (0x08)
\s 空格 (0x20)
\t 制表符
\” 双引号
\’ 单引号
\\ 反斜杠
\ddd 八进制字符 (ddd)
\uxxxx 16进制Unicode字符 (xxxx)

Log4j2.xml Eclipse 添加自动提示

参考:https://issues.apache.org/jira/browse/LOG4J2-411

Support of XSD/DTD linked to the configuration file

It would be very nice, if the the XML configuration uses an dedicated namespace, e.g. http://logging.apache.org/log4j/2.0/config.
This feature allows using XML catalogs to locate the schema locally, e.g. with Eclipse IDE.
The Log4j-events.xsd contains already such a declaration:

targetNamespace="http://logging.apache.org/log4j/2.0/events"

Then the configuration XML file needs only a small extension:

log4j2.xml
<?xml version="1.0" encoding="utf-8"?>
<Configuration status="WARN" xmlns="http://logging.apache.org/log4j/2.0/config">
  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
    </Console>
  </Appenders>
  <Loggers>
    <Root level="error">
      <AppenderRef ref="Console" />
    </Root>
  </Loggers>
</Configuration>

JAVA BIO与NIO、AIO的区别

IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。

一、BIO

在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或者遭到拒绝请求,如果有的话,客户端会线程会等待请求结束后才继续执行。

二、NIO (New IO)

NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题: 在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。

NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知引用程序进行处理,应用再将流读取到缓冲区或写入操作系统。  也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

BIO与NIO一个比较重要的不同,是我们使用BIO的时候往往会引入多线程,每个连接一个单独的线程;而NIO则是使用单线程或者只使用少量的多线程,每个连接共用一个线程。

NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。

在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有BIO一样的问题。

HTTP/1.1出现后,有了Http长连接,这样除了超时和指明特定关闭的http header外,这个链接是一直打开的状态的,这样在NIO处理中可以进一步的进化,在后端资源中可以实现资源池或者队列,当请求来的话,开启的线程把请求和请求数据传送给后端资源池或者队列里面就返回,并且在全局的地方保持住这个现场(哪个连接的哪个请求等),这样前面的线程还是可以去接受其他的请求,而后端的应用的处理只需要执行队列里面的就可以了,这样请求处理和后端应用是异步的.当后端处理完,到全局地方得到现场,产生响应,这个就实现了异步处理。

三、AIO

与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。  即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。  在JDK1.7中,这部分内容被称作NIO.2,主要在Java.nio.channels包下增加了下面四个异步通道:

  • AsynchronousSocketChannel
  • AsynchronousServerSocketChannel
  • AsynchronousFileChannel
  • AsynchronousDatagramChannel

其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。

BIO是一个连接一个线程。

NIO是一个请求一个线程。

AIO是一个有效请求一个线程。

先来个例子理解一下概念,以银行取款为例:

  • 同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写);
  • 异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API);
  • 阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回);
  • 非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)

Java对BIO、NIO、AIO的支持:

  • Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
  • Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
  • Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,

BIO、NIO、AIO适用场景分析:

  • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
  • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
  • AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

另外,I/O属于底层操作,需要操作系统支持,并发也需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。

在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作。

在比较这两个模式之前,我们首先的搞明白几个概念,什么是阻塞和非阻塞,什么是同步和异步,同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪,而异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。

一般来说I/O模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞IO

同步阻塞IO:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!

同步非阻塞IO:在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。

异步阻塞IO:此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!

异步非阻塞IO:在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前Java中还没有支持此种IO模型。