2015年12月30日星期三

Nexus_005:Nexus 2 权限管理

环境: OS X EI Capitan 10.11.2 + JDK 1.8.0_66 + Maven 3.3.9

1. Nexus预定义了三个用户:admin、deployment、anonymous。
(1)admin:该用户拥有对Nexus的完全控制,默认密码为admin123。
(2)deployment:该用户能够访问Nexus,浏览仓库内容,搜索,并上传部署构件,但是无法对Nexus进行任何配置,默认密码为deployment123。
(3)anonymous:该用户对应所有未登录的匿名用户,它们可以浏览仓库并进行搜索。
一个用户可以有一个或多个角色,一个角色可以有一个或多个权限,角色还可以包括角色。


   权限列表:




参考文献:
1. 《Maven 实战》 徐晓斌著  

Nexus_004:手工上传第三方库的构件到 Nexus 2 私服

环境: OS X EI Capitan 10.11.2 + JDK 1.8.0_66 + Maven 3.3.9

1. 选择一个宿主仓库,比如 3rd party ,接着选择 Artifact Upload 
如果该构件是基于Maven的,那么可以选择From POM,否则就选择GAV Parameters。
接着,用户需要手工输入Maven的坐标。

用户可以上传一个主构件和多个附属构件(即Classfier),最后选择页面最下方的Upload Artifact(s)按钮上传到仓库中。




 访问3rd party仓库的url:http://localhost:8081/nexus/content/repositories/thirdparty/,或者直接到目录 sonatype-work/nexus/storage/thirdparty 下都可以看到刚上传的构件。

参考文献:
1. 《Maven 实战》 徐晓斌著 

Nexus_003:配置 Nexus 2 私服

环境: OS X EI Capitan 10.11.2 + JDK 1.8.0_66 + Maven 3.3.9

1. 配置Maven从Nexus下载构件

修改~/.m2/settings.xml,增加profiles部分,定义构件仓库和插件仓库均指向私服,并激活。

<settings>

......
   <profiles>

        <!— Configure the Nexus repository —>

        <profile>
            <id>nexus</id>
            <repositories>
                <repository>
                    <id>nexus</id>
                    <name>Nexus</name>
                    <layout>default</layout>
                    <url>http://localhost:8081/nexus/content/groups/public/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>

            <pluginRepositories>
                <pluginRepository>
                    <id>nexus</id>
                    <name>Nexus</name>
                    <layout>default</layout>
                    <url>http://localhost:8081/nexus/content/groups/public/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </pluginRepository>
            </pluginRepositories>

    </profile>

    <activeProfiles>
        <activeProfile>nexus</activeProfile>
    </activeProfiles>
......
</settings>

2. 配置镜像

为了所有的请求都首先通过私服,修改~/.m2/settings.xml,增加mirrors镜像部分。
<settings>
  ...... 

  <mirrors>
        <mirror>
            <id>nexus</id>
            <mirrorOf>*</mirrorOf>
            <url>http://localhost:8081/nexus/content/groups/public/</url>
        </mirror>
  </mirrors>

  <profile>
            <id>nexus</id>
            <repositories>
                <repository>
                    <id>central</id>
                    <url>http://central/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </repository>
            </repositories>

            <pluginRepositories>
                <pluginRepository>
                    <id>central</id>
                    <url>http://central/</url>
                    <releases>
                        <enabled>true</enabled>
                    </releases>
                    <snapshots>
                        <enabled>true</enabled>
                    </snapshots>
                </pluginRepository>
            </pluginRepositories>

    </profile>
    ......
</settings>

(1)这样配置镜像后,所有的请求都会首先从私服走。
(2)这里重写中央仓库的目的是允许快照版构件的下载,覆盖了超级POM中中央仓库的配置。
其中的url配置无关紧要,因为已经通过私服与连接中央仓库了。
(3)当Maven需要下载构件时,首先检查central,看该类型的构件是否支持,如果支持,再根据镜像匹配原则转而问私服。

3. 配置 Nexus 认证

如果只为代理外部公共仓库,Nexus的代理仓库就已经满足需要了。
组织内部的构件和无法从公共仓库获取的第三方的构件,需要部署在 Nexus上的宿主仓库中。
为了安全起见,需要在~/.m2/settings.xml中设置认证信息。

<servers>

        <server>
            <id>nexus-releases</id>
            <username>admin</username>
            <password>welcome1</password>
        </server>

        <server>
            <id>nexus-snapshots</id>
            <username>admin</username>
            <password>welcome1</password>
        </server>

</servers>

参考文献:
1. 《Maven 实战》 徐晓斌著

2015年12月28日星期一

Nexus_002:使用 Nexus 2 搜索构件

环境: OS X EI Capitan 10.11.2 + JDK 1.8.0_66 + Maven 3.3.9

1. 使用sonatype nexus 提供公有的搜索服务:http://repository.sonatype.org/
 Nexus 提供了关键字搜索、类名搜索、坐标搜索、校验和搜索功能。


搜索后,会清晰的列出构件的坐标及所属仓库。
用户可以直接下载构件,还可以复制根据坐标自动生成的XML依赖声明。

2. 搭建好 nexus 私服后,可以搜索私服上的构件



参考文献:
1. 《Maven 实战》 徐晓斌著  

Nexus_001:使用 Nexus 2 搭建私服

环境: OS X EI Capitan 10.11.2 + JDK 1.8.0_66 + Maven 3.3.9


1. 下载并安装 Nexus
下载地址:http://www.sonatype.org/nexus/。
Nexus 分为社区版和专业版,这里使用社区版。
下载最新的Bundle发布版本:nexus-2.12.0-01-bundle.zip。Bundle版本自带了Jetty容器,不需要额外的Web容器就能直接启动Nexus。
unzip nexus-2.12.0-01-bundle.zip 解压后,有两个目录:
(1)nexus-2.12.0-01:包含了启动Nexus所需要的文件,如启动脚本,依赖jar包等等。该目录是必须要有的。
(2)sonatype-work:该目录包含了Nexus生成的配置文件、日志文件、仓库文件。该目录不是必须的,Nexus会在运行时动态创建该目录,其内容对于各个Nexus实例是不同的。不同的用户在不同的机器上使用Nexus有不同的配置和仓库。

ln -s nexus-2.12.0-01 nexus

2. 启动与停止 Nexus
cd nexus/bin
(1)前台启动 Nexus:./nexus console
(2)停止后台 Nexus:./nexus stop
(3)后台启动 Nexus:./nexus start
(4)查看 Nexus 状态:./nexus status
(5)后台重启 Nexus:./nexus restart

访问:http://localhost:8081/nexus/,管理员账户默认:admin/admin123。
如果想要修改Nexus默认的8081端口,修改nexus/conf/nexus.properties即可。

 
点击左边导航栏中的Repositories链接,可以看到所有类型的Nexus仓库。
仓库有四种类型:group、hosted、proxy、virtual。每个仓库的Format为maven2或maven1,Policy为Release或Snapshot。


Nexus 内置的仓库列表:
(1)Central:代理 Maven 中央仓库。
(2)Releases:策略为 Release 的宿主仓库,用来部署组织内部发布版本的构件。
(3)Snapshots:策略为 Snapshots 的宿主仓库,用来部署组织内部快照版本的构件。
(4)3rd Party:策略为 Release 的宿主仓库,用来部署无法从公共仓库获得的第三方发布版本构件。
(5)Apache Snapshots:代理 Apache Maven 仓库的快照版本构件。
(6)Public Repositories:该仓库将所有上述策略为 Release 的仓库聚合并通过一致的地址来提供服务。


Maven 可以直接从宿主仓库下载;也可以从代理仓库下载,代理仓库会间接地从远程仓库下载并缓存构件;为了方便,也可以从仓库组下载构件,仓库组没有实际内容,它会转向宿主仓库或者代理仓库获取实际的构件。

3.  创建并设置宿主仓库
点击Add...按钮,选择 Hosted Repository,配置信息如下:

 

(1)仓库的ID。
(2)仓库的名称。
(3)仓库的类型。
(4)仓库的格式。
(5)仓库的策略。
(6)仓库的默认存储目录。
(7)仓库的自定义存储目录。
(8)仓库的部署策略:只读(禁止部署)、关闭重新部署(同一构件只能部署一次)、允许重新部署。
(9)是否允许浏览仓库。
(10)是否对该仓库进行索引并提供搜索。
(11)是否允许通过URL访问仓库。
(12)当构件没找到时,缓存这一不存在的信息的时间。默认1440分钟,如果构件没找到,在1440分钟之内再查找时,直接返回不存在,而不会再查找文件系统。

4.  创建并设置代理仓库
点击Add...按钮,选择 Proxy Repository,配置信息如下:


(1)远程仓库的地址ID。
(2)是否下载远程仓库的索引,下载索引后,即使没有那些远程构件,用户也可以在本地搜索浏览那些构件信息。
(3)
(4)
(5)校验出错时的策略。
(6)构件缓存的最长时间。对于发布版本默认为-1,表示自第一次下载后一直有效,不再重新下载;对于快照版本默认为1440,表示每隔一天重新缓存代理构件。

5.  创建并设置仓库
点击Add...按钮,选择 Repository Group,配置信息如下:


(1)仓库组没有Releases 和 Snapshots 的区别。
(2)仓库组中所包含的仓库的顺序决定了仓库组遍历其所含仓库的次序,因此要将常用的仓库放在前面。

参考文献:
1. 《Maven 实战》 徐晓斌著 

2015年12月27日星期日

Maven_004:聚合与继承

环境: OS X EI Capitan 10.11.2 + JDK 1.8.0_66 + Maven 3.3.9

1. 聚合
实际情况中,一个项目是由多个Maven项目组成的,如果每次构建整个项目都要逐一手工构建Maven项目,那就太麻烦了。
聚合就是解决此类问题的:只需运行一次Maven命令,就可以构建整个项目。

在子Maven项目的上一级目录创建一个父目录,比如account-aggregator,作为聚合项目。
该项目只有一个pom.xml,没有其它内容,因为它的作用就是聚合。
各个子Maven项目依然可以独立构建。

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.juvenxu.mvnbook.account</groupId>
    <artifactId>account-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Account Parent</name>
    <modules>
        <module>account-email</module>
        <module>account-persist</module>
    </modules>
</project>

(1)modules 中指定包含的子Maven项目,这里的值是子Maven项目的目录名称。
(2)packaging 必须设置为 pom。
(3)modules 中指定子Maven项目。

完成后的项目目录结构如下:
account-aggregator
    -- account-email
        -- pom.xml
        ...
    -- account-persist
        -- pom.xml
        ... 
    -- pom.xml

2. 继承
聚合解决了使用一条Maven命令构建多个Maven项目的问题。
但实际情况中,多个Maven子项目具有共同的属性,建议把这些相同的属性抽取出来,放到一个父 Maven 项目中声明,然后在各个子Maven项目中继承来自父 Maven 项目中声明。

在子Maven项目的同级目录创建一个目录,比如account-parent,作为父项目。
该项目只有一个pom.xml,没有其它内容,因为它的作用就是继承。

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.juvenxu.mvnbook.account</groupId>
    <artifactId>account-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Account Parent</name>
</project>

(1)packaging 必须设置为 pom。

同时,在各个子Maven项目中,也要指向该父 Maven 项目,以account-email为例:

<project>
    <modelVersion>4.0.0</modelVersion>
   
    <parent>
        <groupId>com.juvenxu.mvnbook.account</groupId>
        <artifactId>account-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../account-parent/pom.xml</relativePath>
    </parent>
   
    <artifactId>account-email</artifactId>
    <name>Account Email</name>
</project>

(1)parent 使用parent元素声明父模块,groupId + artifactId + version 唯一定位父模块。
(2)relativePath 指定父模块pom.xml文件的相对路径。这个设置很重要,如果找不到父模块的pom.xml,子Maven项目将构建失败。
(3)在子Maven项目中不再声明 groupId 和 version,因为已经从父模块中继承了。当然,如果子Maven 项目中的groupId 和 version 与父模块中的声明不同,也可以继续声明,覆盖父模块中的声明。

最后,在 account-aggregator 项目中还要增加 account-parent 模块。

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.juvenxu.mvnbook.account</groupId>
    <artifactId>account-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Account Parent</name>
    <modules>
        <module>account-parent</module>
        <module>account-email</module>
        <module>account-persist</module>
    </modules>
</project>

完成后的项目目录结构如下:
account-aggregator
    -- account-email
        -- pom.xml
        ...
    -- account-persist
        -- pom.xml
        ...
    -- account-parent
        -- pom.xml
    -- pom.xml

3. 其它可继承的POM元素
除了 groupId 和 version,还有很多元素可以继承,其中最重要的是依赖关系:dependencies。
直接把dependicies加到父Maven项目中是可以的,但是还不够灵活,因为不是每个子Maven项目都需要这些依赖。
dependencyManagement 既可以让子模块继承到父模块的以来配置,又能保证子模块依赖使用的灵活性。
在dependencyManagement中声明的依赖不会引入实际的依赖,但是可以约束dependencies的使用。
如果子模块不声明依赖的使用,即使该依赖在父模块的 dependencyManagement 中声明了,还是不会起作用。
要引入实际的依赖,还是要在dependencies元素中定义才有效。

在父Maven项目:account-parent 中加入 dependencyManagement 配置信息:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.juvenxu.mvnbook.account</groupId>
    <artifactId>account-parent</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <name>Account Parent</name>
    <properties>
        <springframework.version>2.5.6</springframework.version>
        <junit.version>4.7</junit.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${springframework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-beans</artifactId>
                <version>${springframework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>${springframework.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context-support</artifactId>
                <version>${springframework.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

此时,在子Maven项目:account-email 中的依赖配置如下:

<project>
    <modelVersion>4.0.0</modelVersion>
   
    <parent>
        <groupId>com.juvenxu.mvnbook.account</groupId>
        <artifactId>account-parent</artifactId>
        <version>1.0.0-SNAPSHOT</version>
        <relativePath>../account-parent/pom.xml</relativePath>
    </parent>
   
    <artifactId>account-email</artifactId>
    <name>Account Email</name>

    <properties>
        <javax.mail.version>1.4.1</javax.mail.version>
        <greenmail.version>1.3.1b</greenmail.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>       
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>           
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>           
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>           
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>${javax.mail.version}</version>
        </dependency>       
        <dependency>
            <groupId>com.icegreen</groupId>
            <artifactId>greenmail</artifactId>
            <version>${greenmail.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

(1)子Maven项目中的依赖只需定义groupId 和 artifactId,因为version 和 scope 已经从父Maven项目中继承了,从而保证所有子Maven项目使用的依赖是同一个版本,降低依赖冲突。
(2)子Maven项目中可以继续详细定义没有在父Maven项目中定义的依赖。
(3)在子Maven项目中不再声明 groupId 和 version,因为已经从父模块中继承了。
当然,如果子Maven 项目中的groupId 和 version 与父模块中的声明不同,也可以继续声明,覆盖父模块中的声明。

4. 导入 dependencyManagement 中声明的依赖
还记得依赖关系中有个import的scope吧,这个范围的依赖只在 dependencyManagement 元素中才有意义。

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.juvenxu.mvnbook.account</groupId>
                <artifactId>account-parent</artifactId>
                <version>1.0.0-SNAPSHOT</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>          
        </dependencies>
</dependencyManagement>

(1)导入account-parent 模块中定义的dependencyManagement 内容。
(2)如果有多个项目使用的依赖版本都是一致的,则可以定义一个使用 dependencyManagement 专门管理依赖的POM,然后在各个项目中导入这个依赖。

5. 使用 pluginManagement 管理插件
与 dependencyManagement 使用方法类似,Maven使用pluginManagement 管理插件。
在父模块中声明可以使用的插件,在子模块中声明要使用的插件。

参考文献:
1. 《Maven 实战》 徐晓斌著

2015年12月26日星期六

Maven_003:设置本地仓库和远程仓库

环境: OS X EI Capitan 10.11.2 + JDK 1.8.0_66 + Maven 3.3.9

构件在仓库中的路径为: groupId/artifactId/version/artifactId-version-classifier.packaging。

Maven仓库分为本地仓库和远程仓库这两种,首先从本地仓库去找构件,如果存在,则直接使用;如果不存在,去远程仓库查找,找到后,下载到本地仓库。
如果本地仓库和远程仓库都没有找到需要的构件,Maven会报错。
中央仓库Maven核心自带的远程仓库,默认情况下,当本地仓库没有构件的时候,就会从中央仓库下载。
私服是一种特殊的远程仓库,是假设在局域网内的私有仓库,用其代理所有的远程仓库。
除了中央仓库和私服,还有很多其它公开的远程仓库,比如JBoss Maven 库:http://repository.jboss.org/maven2/。

1. 设置本地仓库
默认情况下,本地仓库指向~/.m2/repository目录。
要指向其它目录,需要修改~/.m2/settings.xml文件,增加如下内容:
<!-- localRepository
   | The path to the local repository maven will use to store artifacts.
   |
   | Default: ${user.home}/.m2/repository
  <localRepository>/path/to/local/repo</localRepository>
-->

2. 私服
私服是指在局域网内部搭建的 repository,它跟 central repository,jboss repository等的区别仅仅在于其 URL 是一个内部网址。

3. 设置远程仓库
远程仓库既可以设置在pom.xml中,也可以设置在settings.xml文件中。

 <project>
    ...
    <repositories>
        <repository>
          <id>jdk14</id>
          <name>Repository for JDK 1.4 builds</name>
          <url>http://www.myhost.com/maven/jdk14</url>
          <layout>default</layout>
          <snapshotPolicy>always</snapshotPolicy>
        </repository>
    </repositories>
    ...
 </project>

(1)仓库的id必须唯一。
(2)releases的enable值为true,表示开启发布版本的下载支持。
(3)snapshots的enable值为false,表示关闭快照版本的下载支持。
(4)layout 表示使用Maven 2 或 3 的布局。
(5)updatePolicy 表示从远程仓库的更新频率,默认是daily,每天检查;never,从不检查;always,每次构建都检查更新;X,每隔X分钟检查一次。
(6)checksumPolicy 表示部署构件时,会同时部署对应的校验文件;下载构件时,会验证校验文件。该值为 warn 时,提示警告;为 fail 时,构建失败;为 ignore 时,忽略错误。 

4. 配置远程仓库的认证
远程仓库的认证只能设置在settings.xml文件中。

<servers>
    <!-- server
     | Specifies the authentication information to use when connecting to a particular server, identified by
     | a unique name within the system (referred to by the 'id' attribute below).
     |
     | NOTE: You should either specify username/password OR privateKey/passphrase, since these pairings are
     |       used together.
     |
    <server>
      <id>deploymentRepo</id>
      <username>repouser</username>
      <password>repopwd</password>
    </server>
    -->

    <!-- Another sample, using keys to authenticate.
    <server>
      <id>siteServer</id>
      <privateKey>/path/to/private/key</privateKey>
      <passphrase>optional; leave empty if not used.</passphrase>
    </server>
    -->
</servers>

5. 部署构件到远程仓库
在pom.xml中设置远程仓库:

<distributionManagement>
    <repository>
        <id>releases</id>
        <name>Nexus Release Repository</name>
        <url>http://localhost:8081/nexus/content/repositories/releases/</url>
    </repository>
    <snapshotRepository>
        <id>snapshots</id>
        <name>Nexus Snapshot Repository</name>
        <url>http://localhost:8081/nexus/content/repositories/snapshots/</url>
    </snapshotRepository>
</distributionManagement>

(1)releases 是发布版本的构件,项目外部的依赖应该都是发布版本的构件。
(2)snapshots 是快照版本的构件,快照版本的构件只应该用于项目内部的模块依赖。
(3)repository 中的 id 必须与 server 中的 id 一致。

运行 mvn clean deploy,部署构件到远程仓库,发布版本会部署到发布版本的远程仓库;快照版本会部署到快照版本的远程仓库。

6. 镜像
仓库 X 能够提供仓库 Y 的所有内容,那么仓库 X 就是仓库 Y 的一个镜像。
配置镜像后,本来需要从仓库 Y 下载构件的请求会转到仓库 X。
在 settings.xml 中增加镜像的配置:

例1:指定仓库的镜像
<mirrors>
    <!-- mirror
     | Specifies a repository mirror site to use instead of a given repository. The repository that
     | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
     | for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
     |
    <mirror>
      <id>mirrorId</id>
      <mirrorOf>repositoryId</mirrorOf>
      <name>Human Readable Name for this Mirror.</name>
      <url>http://my.repository.com/repo/path</url>
    </mirror>
     -->
</mirrors>
说明:
(1)mirrorOf 指定镜像的repositoryId。也可以以逗号间隔,指定多个远程库。

例2:中央仓库的镜像
<settings>
  ...
  <mirrors>
    <mirror>
      <id>maven.net.cn</id>
      <name>one of the central mirrors in china</name>
      <url>http://maven.net.cn/content/groups/public/</url>
      <mirrorOf>central</mirrorOf>
    </mirror>
  </mirrors>
  ...
</settings>
说明:
(1)mirrorOf 指定镜像的repositoryId,这里 central 表示中央仓库。也可以以逗号间隔,指定多个远程库。

例3:所有仓库的镜像
关于镜像的一个更为常见的用法是结合私服。由于私服可以代理任何外部的公共仓库,因此,对于组织内部的 Maven 用户来说,使用一个私服地址就等于使用了所有需要的外部仓库,这可以将配置集中到私服,从而简化 Maven 本身的配置。在这种情况下,任何需要的构件都可以从私服获得,私服就是所有仓库的镜像。  

<settings>
  ...
  <mirrors>
    <mirror>
      <id>internal-repository</id>
      <name>Internal Repository Manager</name>
      <url>http://192.168.1.100/maven2</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
  ...
</settings>

说明:
(1) <mirrorOf>*</mirrorOf> ,表示该配置是所有 Maven 仓库的镜像,任何对于远程仓库的请求都会被转至 http://192.168.1.100/maven2/。
如果该镜像仓库需要认证,则配置一个 id 为 internal-repository 的 <server> 即可。
(2) <mirrorOf>external:*</mirrorOf> ,表示匹配所有远程仓库,使用localhost 的除外,使用 file:// 协议的除外。也就是说,匹配所有不在本机上的远程仓库。
(3) <mirrorOf>repo1,repo2</mirrorOf> ,表示匹配仓库 repo1 和 repo2,使用逗号分隔多个远程仓库。 
(4) <mirrorOf>*,!repo1</miiroOf> ,表示匹配所有远程仓库,repo1除外,使用感叹号将仓库从匹配中排除。

需要注意的是,由于镜像仓库完全屏蔽了被镜像仓库,当镜像仓库不稳定或者停止服务的时候,Maven 将无法访问被镜像仓库,因此也将无法下载构件。

7. 私服与镜像的区别
私服本身是一个 repository,可以和其它 repository 一起提供服务,比如它可以用来提供公司内部的 maven 构件。
镜像相当于一个代理,它会拦截去指定的远程 repository 下载构件的请求,然后从自己这里找出构件回送给客户端。镜像本身并不是 repository,它只是远程 repository 的网络加速器,配置镜像的目的一般是出于网速考虑。

可以看出,私服和镜像两个不同的东西。
不过很多私服搭建工具本身也提供镜像服务,比如 Nexus 就可以让同一个 URL 既用作internal repository,又使它成为所有 repository 的 mirror。

参考文献:
1. 《Maven 实战》 徐晓斌著

2015年12月25日星期五

Maven_002:构建项目

环境: OS X EI Capitan 10.11.2 + JDK 1.8.0_66 + Maven 3.3.9

1. pom.xml

Maven项目的核心是pom.xml文件,POM(Project Object Model)定义了项目的基本信息,描述了项目如何构建,声明了项目的依赖关系,等等。
下面是一个helloworld的pom.xml文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mycompany</groupId>
    <artifactId>helloworld</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>Hello World</name>
    <packaging>jar</packaging>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
</project>

(1)modelVersion 指定了当前POM的版本,对于Maven 3 来说,该值只能是4.0.0。
(2)groupId 指定了项目所属的组,一般为某个公司名称或组织名称 + 项目名称,表明当前项目隶属的实际项目,比如org.sonatype.nexus。
(3)artifactId 指定了项目在该组中的唯一标识,一般为实际项目中的一个Maven项目,推荐使用实际的项目名称作为前缀,比如nexus-indexer。
注意,Maven项目和实际项目不一定是一对一的关系,很多实际项目可能对应很多Maven项目。
(4)version 指定了项目的版本。
(5)name 指定了项目的名称,该项不是必须的,但为了可读友好性,还是建议加上该项。
(6)packaging 指定了Maven项目的打包方式,比如 jar(默认方式),war。
(7)calssfier 指定了输出的附属构件,比如 javadoc、sources。附属构件不是项目默认生成的,需要配置相应的插件才可以。

groupId + artifactId + version 定义了一个项目的基本的x、y、z坐标,每一个Maven项目都可以通过这三个坐标来彼此区分。

所有的Maven项目都隐式地继承一个超级POM,对于Maven 3,该文件位于 $MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml。

2. 项目依赖

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.7</version>
        <scope>test</scope>
    </dependency>
</dependencies>

(1)dependencies 里面可以有多个 dependency,表示项目的依赖。
(2)scope 为依赖范围:
compile(默认选项)表示该依赖对编译、测试、运行的 classpath 都有效,比如 spring-core;
test 表示该依赖只对测试的 classpath 有效,比如 JUnit;
provided  表示该依赖对编译、测试的 classpath 都有效,对运行的 classpath 无效,比如 servlet-api,在编译和测试时需要该依赖,在运行时,因为容器已经提供,因此就不需要了;
runtime 表示该依赖对测试、运行的 classpath 都有效,对编译的 classpath 无效,比如 JDBC驱动,编译时只需要标准的JDBC接口API即可,测试和运行时才需要具体的JDBC驱动;
system 与 provided 方式的依赖一样,只不过指定依赖时,必须通过systemPath显式指定,往往与本机系统绑定,造成不可移植,应慎用。
import 导入依赖范围,该依赖不会对编译、测试、运行的 classpath 有任何实际的影响。
(3)optional
(4)exclusions

关于依赖,还有很多概念需要了解,比如传递性依赖、依赖调解、可选依赖、排除依赖、归类依赖等等,留待以后专文阐述。

要想查看一个Maven项目的依赖,可以运行 mvn:dependency:list 或 mvn:dependency:tree 或 mvn:dependency:analyze。

2. 编译、测试、打包、安装
 (1)mvn clean compile
 (2)mvn clean test
 (3)mvn clean package
该命令会在 target 目录下生成 artifactId + version.jar 文件,也可能是其它类型的文件,比如war文件。
 (4)mvn clean install
为了让其它Maven 项目能够引用该 jar,需要运行该命令将该Maven项目安装到本地库。

3.  使用maven-shade-plugin
我们知道如果要运行.jar中的某个带有Main函数的类,需要在META-INFO目录中创建MANIFEST.MF文件。
文件内容要增加一行:Main-Class: com.juvenxu.mvnbook.helloworld.HelloWorld。
手工增加太麻烦了,为此使用 Maven 插件:maven-shade-plugin。
<plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
            <source>1.5</source>
            <target>1.5</target>
          </configuration>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-shade-plugin</artifactId>
          <version>1.2.1</version>
          <executions>
            <execution>
              <phase>package</phase>
              <goals>
                <goal>shade</goal>
              </goals>
              <configuration>
                <transformers>
                  <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                    <mainClass>com.juvenxu.mvnbook.helloworld.HelloWorld</mainClass>
                  </transformer>
                </transformers>
              </configuration>
            </execution>
          </executions>
        </plugin>
</plugins>

使用 maven-compiler-plugin 的目的是为了支持JDK 1.5。

4. 使用 mvn archetype:generate 生成Maven项目骨架 
运行后,将会以交互的方式生成一个Maven项目,目前有1497个Maven项目骨架。
首先输入骨架对应的数字,默认是704,Maven的quickstart项目骨架。
接着输入 groupId、artifactId、version 等值,生成Maven项目。
常用的Maven骨架有maven-archetype-quickstart (Java Project, JAR)、maven-archetype-webapp (Java Web Project, WAR)等等。

5. 使用 mvn archetype:crawl 生成骨架目录文件
1497个Maven骨架太多了,而且不知道选择哪个,可以把所有内容写到一个文件中,然后慢慢查找。
mvn archetype:generate > templates.txt

也可以生成 archetype-catalog 目录,因为实际使用中常用的就是那几个。
运行 mvn archetype:crawl 后,会在~/.m2/responsitory目录下生成archetype-catalog.xml文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<archetype-catalog xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0 http://maven.apache.org/xsd/archetype-catalog-1.0.0.xsd"
    xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <archetypes>
    <archetype>
      <groupId>org.apache.maven.archetypes</groupId>
      <artifactId>maven-archetype-quickstart</artifactId>
      <version>1.1</version>
      <description>quickstart</description>
    </archetype>
  </archetypes>
</archetype-catalog>

把 archetype-catalog.xml 移动到~/.m2目录下。
运行 mvn archetype:generate -DarchetypeCatalog=local,此时只会显示archetype-catalog.xml文件中的骨架。

另外,也可以运行 mvn archetype:generate -DarchetypeCatalog=internal,此时会显示10个内部骨架。

6. 约定优于配置
Maven 的默认约定如下:
(1)源码目录为:src/main/java
(2)编译输出目录为:target/classes
(3)打包方式为:jar
(4)包输出的目录为:target

参考文献:
1. 《Maven 实战》 徐晓斌著
2.  http://www.cnblogs.com/buhaiqing/archive/2012/11/04/2754187.html

Maven_001:下载与安装配置

环境: OS X EI Capitan 10.11.2 + JDK 1.8.0_66 + Maven 3.3.9

1. 下载 http://maven.apache.org/

2. 安装 unzip apache-maven-3.3.9-bin.zip

3. 配置
(1)ln -s apache-maven-3.3.9 maven
(2)环境变量脚本

#!/bin/sh

#Set Java Environment
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_66.jdk/Contents/Home
export CLASSPATH=.:$JAVA_HOME/lib/tools.jar
export ANT_HOME=/Users/maping/Apache/ant
export MAVEN_HOME=/Users/maping/Apache/maven
export M2_HOME=/Users/maping/Apache/maven
export PATH=$JAVA_HOME/bin:$ANT_HOME/bin:$M2_HOME/bin:$MAVEN_HOME/bin:$PATH

说明:
MAVEN_HOME 用于 Maven 1.x,M2_HOME 用于 Maven 2.x 和 3.x。

(3)修改Maven使用的内存
Maven运行时,实际是调用的Java,因此设置内存方式和Java一样。

export MAVEN_OPTS= -Xms128m -Xmx512m

(4)为了保证使用同一个Maven,IDE也不要使用自带的Maven,而是指向同一个MAVEN_HOME。
这里以NetBeans为例说明:



4. 执行 mvn -v,确认安装配置正确。
也可以运行 mvn help:system,查看本地环境。

5. 全局性配置文件:setttings.xml
在conf目录下有一个非常重要的文件:setttings.xml,该文件是Maven的全局性配置文件,所有该机器上的用户都会受到此文件的影响。
一般情况下,我们不修改该位置的settings.xml文件,而是复制该文件到~/.m2/目录下后,修改该文件,在用户的范围内定制Maven的行为。

6. Maven本地仓库
在~/.m2/目录下有一个repository目录,该目录就是该用户的Maven本地仓库。

7. 设置代理
如果无法直接访问Maven 中央库,可以设置代理,步骤如下:
(1)确认是否能够访问中央库:ping repo1.maven.org
(2)确认是否能够访问代理:telnet
(3)编辑~/.m2/settings.xml,增加如下内容:

  <proxies>
    <!-- proxy
     | Specification for one proxy, to be used in connecting to the network.
     |
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>proxyuser</username>
      <password>proxypass</password>
      <host>proxy.host.net</host>
      <port>80</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
    -->
  </proxies>

proxies中可以包含多个proxy,第一个被激活( <active>true</active>)的proxy将被使用。
nonProxyHosts 指定哪些主机名不需要代理,支持通配符,比如:*.google.com。

8. 查看本地环境
运行 mvn help:system

参考文献:
1. 《Maven 实战》 徐晓斌著

JDG_014:缓存节点规划以及容量估算

假定每个JDG节点的堆内存容量估算后的大小为H,那么其推算过程如下:

1. 期望每个JDG节点的堆内存大小是多少?(变量=>S)

2. 估算一条数据的长度。(变量=>x)
  (1)x = 序列化后的Key + 序列化后的Value + 200 bytes 元数据

3. 期望多少条数据放入缓存中?(变量=>y)

4. 考虑到数据同步与计算的冗余,需要给堆内存留出一部分“空闲”空间。(变量=>p,默认0.5)

5. 部署模式是哪种?Local,Distributed,还是 Replicated mode?
    (1)如果是 Local 或 Replicated mode,那么 H = (x * y) / p,这里 S 必须大于 H。
    (2)如果是 Distributed mode,owners=n(变量=>n),那么1份数据需要的JDG节点数是 (x * y) / (S * p) + 1,n份数据需要的JDG节点数是 ((x * y) / (S * p) + 1) * n。

现在我们根据真实使用案例,实际推算一下。
(1)S = 32 G
(2)x = 10 + 35 + 200 = 245 bytes
(3)y = 1,000,000,000 (10 亿条)
(4)p = 0.5
(5)Distributed mode,owners = 3

数据总量是 245 * 1000000000 = 245 G
每个JDG节点可用来存储数据的内存 = 32 * 0.5 = 16 G
1份数据需要的JDG节点数= ((245 * 1) / (32 * 0.5) + 1) = 16
3份数据需要的JDG节点数= ((245 * 1) / (32 * 0.5) + 1) * 3 = 48

参考文献:
1. https://mojo.redhat.com/docs/DOC-174679

2015年12月18日星期五

JDG_013:JDG CLI 使用指南

环境:MAC OS 10.10.5 + JDG 6.5.1

进入 jboss-datagrid-6.5.1-server/bin 目录,

1. ./jboss-cli.sh --connect=jmx://127.0.0.1:10099
默认端口是9999,这里端口偏移了100,变成10099。
如果连接成功,会自动进入当前cache所在的cache-container,比如我这里是clustered。
[standalone@127.0.0.1:10099 cache-container=clustered]

2.  在[standalone@127.0.0.1:10099 cache-container=clustered] 处按“Tab”键,会显示能做的操作
(1)输入container,会显示有哪些cache-container。
(2)输入cache + “Tab”键,会显示有哪些cache,比如我这里会显示:
          TRANSPORT       default         labCache        memcachedCache  namedCache
(3)输入cache labCache,进入labCache:[standalone@127.0.0.1:10099 distributed-cache=labCache]
(4)输入stats,显示当前cache的统计信息

[standalone@127.0.0.1:10099 distributed-cache=labCache] stats
Statistics: {
  hits: 0
  evictions: 0
  hitRatio: 0.0
  removeHits: 0
  readWriteRatio: 0.0
  misses: 0
  averageRemoveTime: 0
  averageReadTime: 0
  numberOfEntries: 67
  averageWriteTime: 1
  statisticsEnabled: true
  elapsedTime: 58461
  removeMisses: 0
  stores: 65
  timeSinceReset: 58461
}
LockManager: {
  numberOfLocksAvailable: 0
  numberOfLocksHeld: 0
  concurrencyLevel: 1000
}
RpcManager: {
  committedViewAsString: [jdg-2/clustered, jdg-1/clustered, jdg-3/clustered]
  statisticsEnabled: true
  replicationFailures: 0
  successRatioFloatingPoint: 1.0
  replicationCount: 42
  averageReplicationTime: 2
  successRatio: 100%
  pendingViewAsString: null
}

(5)输入put 1 1,插入一条数据。
(6)输入get 1,显示刚插入的数据。
(7)输入encoding --list,显示都有哪些encoding。
memcached
hotrod
none
rest
(8)输入get --codec=hotrod 1,显示程序插入的Value。

参考文献:
1. https://docs.jboss.org/author/display/ISPN/Command-Line+Interface+%28CLI%29

2015年12月16日星期三

JDG_012:JDG Demo 演示:jdg-visualizer

环境:MAC OS 10.10.5 + JDG 6.5.1 + JBoss EAP 6.4.0

jdg-visualizer 可以监控JDG集群中各个节点的数据数量分布情况。

1. 下载 jdg-visualizer
下载地址:https://github.com/infinispan/visual

2. 下载 hotrod-demo
hotrod-demo 可以向集群中某个节点插入数据,从而导致整个集群的数据重新分布。
下载地址:https://github.com/saturnism/hotrod-demo/

3. 编译 jdg-visualizer
mvn clean package
编译成功后会生成 jdg-vistualizer.war 文件。

4. 编译 hotrod-demo
mvn clean package
编译成功后会生成 hotrod-demo-0.0.2-SNAPSHOT-jar-with-dependencies.jar 文件。

5. 配置并启动JDG集群
(1)unzip jboss-datagrid-6.5.1-server.zip
(2)执行./add-user.sh,增加管理员用户 admin/welcome@1
(3)mkdir jdg-visualizer-node1
(4)cp -r ./jboss-datagrid-6.5.1-server/standalone jdg-visualizer-node1
(5)mkdir jdg-visualizer-node2
(6)cp -r ./jboss-datagrid-6.5.1-server/standalone jdg-visualizer-node2
(7)mkdir jdg-visualizer-node3
(8)cp -r ./jboss-datagrid-6.5.1-server/standalone jdg-visualizer-node3
(9)./clustered.sh -Djboss.server.base.dir=/Users/maping/Redhat/datagrid/jdg-visualizer-node1/standalone -c clustered-jdg-visualizer.xml -b 127.0.0.1 -bmanagement=127.0.0.1 -Djboss.node.name=jdg-1 -Djboss.socket.binding.port-offset=100
(10)./clustered.sh -Djboss.server.base.dir=/Users/maping/Redhat/datagrid/jdg-visualizer-node2/standalone -c clustered-jdg-visualizer.xml -b 127.0.0.1 -bmanagement=127.0.0.1 -Djboss.node.name=jdg-2 -Djboss.socket.binding.port-offset=200
(13)./clustered.sh -Djboss.server.base.dir=/Users/maping/Redhat/datagrid/jdg-visualizer-node3/standalone -c clustered-jdg-visualizer.xml -b 127.0.0.1 -bmanagement=127.0.0.1 -Djboss.node.name=jdg-3 -Djboss.socket.binding.port-offset=300
(14)其中 clustered-jdg-visualizer.xml 配置如下(分布式模式,并使用tcp):
 <distributed-cache name="labCache" mode="SYNC" owners="2" remote-timeout="30000" start="EAGER">
    <locking isolation="READ_COMMITTED" acquire-timeout="30000" concurrency-level="1000" striping="false"/>
    <transaction mode="NONE"/>
 </distributed-cache>

 6. 部署 jdg-visualizer.war 到 EAP,并启动EAP
 (1)./standalone.sh -b 127.0.0.1 -bmanagement=127.0.0.1 -Djdg.visualizer.jmxUser=admin -Djdg.visualizer.jmxPass=welcome@1 -Djdg.visualizer.serverList=localhost:11322
注意这里要设置连接到某个JDG节点的JMX用户名和口令,就是之前配置JDG时设置的。
只要连接到其中一个JDG节点即可,EAP会自动发现集群中的其它节点。

7. 访问 http://localhost:8080/jdg-visualizer/
 (1)没有任何数据的情况下,是这个样子的:


 (2)向某个JDG节点插入数据,运行
java -Djdg.demo.initialList=localhost:11322 -Djdg.demo.cacheName=labCache -Djdg.demo.maxEntries=100 -Djdg.demo.clearOnFinish=false -Djdg.demo.putDelay=0 -Djdg.demo.useTwitter=false -jar hotrod-demo-0.0.2-SNAPSHOT-jar-with-dependencies.jar


 (3)停掉一个节点

 (4)再停掉一个节点

参考文献:
1. https://github.com/infinispan/visual
2. https://github.com/saturnism/hotrod-demo/

2015年10月14日星期三

AMQ_005:A-MQ 功能演示5:Master/Slave + Networks Broker 高可用可扩展架构

环境:JBoss A-MQ 6.0.0

组合一下Master/Slave Failover 和 Networks Broker,就诞生了现在的高可用可扩展架构。

(1)在root container下创建两个子container
fabric:container-create-child root AMQ-East 2
输出如下:
The following containers have been created successfully:
    Container: AMQ-East1.
    Container: AMQ-East2.

(2)在root container下创建两个子container
fabric:container-create-child root AMQ-West 2
输出如下:
The following containers have been created successfully:
    Container: AMQ-West1.
    Container: AMQ-West2.

(3)创建 East container,East container 由一对 master/slave broker 组成,属于 amq-east 组,并且和 amq-west broker 以networks方式连接
fabric:mq-create --group amq-east --networks amq-west --networks-username admin --networks-password admin --assign-container AMQ-East1,AMQ-East2 amq-east-profile
输出如下:
MQ profile amq-east-profile ready
Profile successfully assigned to AMQ-East1
Profile successfully assigned to AMQ-East2

(4)创建 West container,West container 由一对 master/slave broker 组成,属于 amq-west 组,并且和 amq-east broker 以networks方式连接
fabric:mq-create --group amq-west --networks amq-east --networks-username admin --networks-password admin --assign-container AMQ-West1,AMQ-West2 amq-west-profile
输出如下:
MQ profile amq-west-profile ready
Profile successfully assigned to AMQ-West1
Profile successfully assigned to AMQ-West2

(5)查看fabric集群情况
fabric:cluster-list
输出如下:
[cluster]                      [masters]                      [slaves]                       [services]
fusemq/amq-east                                                                            
   amq-east-profile            AMQ-East2                      AMQ-East1                      tcp://MaPingdeMacBook-Pro.local:57047
fusemq/amq-west                                                                            
   amq-west-profile           AMQ-West1                     AMQ-West2                      tcp://MaPingdeMacBook-Pro.local:57050
fusemq/a-mq-east                                                                           
   a-mq-east-broker            A-MQ-East                      -                              tcp://MaPingdeMacBook-Pro.local:55602
fusemq/a-mq-west                                                                           
   a-mq-west-broker           A-MQ-West                     -                              tcp://MaPingdeMacBook-Pro.local:55617

(6)生产者
java -jar mq-client.jar consumer --user admin --password admin --brokerUrl "discovery:(fabric:amq-west)"

(7)消费者
java -jar mq-client.jar producer --user admin --password admin --brokerUrl "discovery:(fabric:amq-east)"

参考文献:
1. https://github.com/FuseByExample/external-mq-fabric-client/blob/master/docs/fabric-ha-setup-master-slave.md

AMQ_004:A-MQ 功能演示4:Networks Broker 负载均衡架构

环境:JBoss A-MQ 6.0.0

Master/Slave架构解决了容错和高可用问题,但是没有解决负载均衡和横向扩展性问题。

1. Networks Broker
两个Broker通过networks连接在一起,这两个Broker分别使用不同的persistence stores。
通过networks连接在一起的Broker,意味着它们之间可以为某个Desitination转发消息。
而客户端只需连接任意一个Broker即可。

(1)创建 A-MQ-East container,属于 a-mq-east 组,并且和 a-mq-west broker 以networks方式连接
fabric:mq-create --group a-mq-east --networks a-mq-west --networks-username admin --networks-password admin --create-container A-MQ-East a-mq-east-broker
输出如下:
MQ profile a-mq-east-broker ready
Successfully created container A-MQ-East
(2)创建 A-MQ-West container,属于 a-mq-west 组,并且和 a-mq-east broker 以networks方式连接
fabric:mq-create --group a-mq-west --networks a-mq-east --networks-username admin --networks-password admin --create-container A-MQ-West a-mq-west-broker
输出如下:
MQ profile a-mq-west-broker ready
Successfully created container A-MQ-West

以上两步创建了两个Containers:A-MQ-East、A-MQ-West,以及这两个Containers对应的两个Profiles:a-mq-east-broker、a-mq-west-broker。
每个 Container 运行一个内嵌的 ActiveMQ 实例,它们之间以networks方式连接在一起。

(3)生产者
java -jar mq-client.jar producer --brokerUrl discovery:fabric:a-mq-east --user admin --password admin --destination queue://summit.test --count 10
输出如下:
Using destination: queue://summit.test, on broker: discovery:fabric:a-mq-east
[org.fusesource.mq.fabric.FabricDiscoveryAgent] : Using local ZKClient
[org.fusesource.fabric.zookeeper.internal.AbstractZKClient] : Starting StateChangeDispatcher
[org.apache.zookeeper.ZooKeeper] : Client environment:zookeeper.version=3.4.3-1240972, built on 02/06/2012 10:48 GMT
[org.apache.zookeeper.ZooKeeper] : Client environment:host.name=192.168.56.1
[org.apache.zookeeper.ZooKeeper] : Client environment:java.version=1.7.0_80
[org.apache.zookeeper.ZooKeeper] : Client environment:java.vendor=Oracle Corporation
[org.apache.zookeeper.ZooKeeper] : Client environment:java.home=/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/jre
[org.apache.zookeeper.ZooKeeper] : Client environment:java.class.path=mq-client.jar
[org.apache.zookeeper.ZooKeeper] : Client environment:java.library.path=/Users/maping/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
[org.apache.zookeeper.ZooKeeper] : Client environment:java.io.tmpdir=/var/folders/3w/hlhsjmns5m9g6xv6xqk2864h0000gn/T/
[org.apache.zookeeper.ZooKeeper] : Client environment:java.compiler=
[org.apache.zookeeper.ZooKeeper] : Client environment:os.name=Mac OS X
[org.apache.zookeeper.ZooKeeper] : Client environment:os.arch=x86_64
[org.apache.zookeeper.ZooKeeper] : Client environment:os.version=10.10.5
[org.apache.zookeeper.ZooKeeper] : Client environment:user.name=maping
[org.apache.zookeeper.ZooKeeper] : Client environment:user.home=/Users/maping
[org.apache.zookeeper.ZooKeeper] : Client environment:user.dir=/Users/maping/Redhat/amq/demo/jboss-a-mq-6.0.0.redhat-024/extras
[org.apache.zookeeper.ZooKeeper] : Initiating client connection, connectString=localhost:2181 sessionTimeout=10000 watcher=org.fusesource.fabric.zookeeper.internal.ZKClient@72cf2e49
[org.apache.zookeeper.ClientCnxn] : Opening socket connection to server /127.0.0.1:2181
[org.apache.zookeeper.client.ZooKeeperSaslClient] : Client will not SASL-authenticate because the default JAAS configuration section 'Client' could not be found. If you are not using SASL, you may ignore this. On the other hand, if you expected SASL to work, please fix your JAAS configuration.
[org.apache.zookeeper.ClientCnxn] : Socket connection established to localhost/127.0.0.1:2181, initiating session
[org.apache.zookeeper.ClientCnxn] : Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x150656962ea0009, negotiated timeout = 10000
[org.apache.activemq.transport.discovery.DiscoveryTransport] : Adding new broker connection URL: tcp://MaPingdeMacBook-Pro.local:55602
[org.apache.activemq.transport.failover.FailoverTransport] : Successfully connected to tcp://MaPingdeMacBook-Pro.local:55602
[org.fusesource.mq.ProducerThread] : Sent: test message: 0
[org.fusesource.mq.ProducerThread] : Sent: test message: 1
[org.fusesource.mq.ProducerThread] : Sent: test message: 2
[org.fusesource.mq.ProducerThread] : Sent: test message: 3
[org.fusesource.mq.ProducerThread] : Sent: test message: 4
[org.fusesource.mq.ProducerThread] : Sent: test message: 5
[org.fusesource.mq.ProducerThread] : Sent: test message: 6
[org.fusesource.mq.ProducerThread] : Sent: test message: 7
[org.fusesource.mq.ProducerThread] : Sent: test message: 8
[org.fusesource.mq.ProducerThread] : Sent: test message: 9
[org.fusesource.mq.ProducerThread] : Producer thread finished
Produced: 10
[org.fusesource.mq.ActiveMQService] : Closed JMS connection
[org.fusesource.fabric.zookeeper.internal.AbstractZKClient] : StateChangeDispatcher terminated.
[org.apache.activemq.transport.failover.FailoverTransport] : Transport (tcp://192.168.56.1:55602) failed, reason:  java.io.EOFException, attempting to automatically reconnect
[org.apache.zookeeper.ZooKeeper] : Session: 0x150656962ea0009 closed
[org.apache.zookeeper.ClientCnxn] : EventThread shut down
[org.apache.activemq.transport.failover.FailoverTransport] : Successfully reconnected to tcp://MaPingdeMacBook-Pro.local:55602

(4)消费者
java -jar mq-client.jar consumer --brokerUrl discovery:fabric:a-mq-west --user admin --password admin --destination queue://summit.test --count 10
输出如下:
Using destination: queue://summit.test, on broker: discovery:fabric:a-mq-west
[org.fusesource.mq.fabric.FabricDiscoveryAgent] : Using local ZKClient
[org.fusesource.fabric.zookeeper.internal.AbstractZKClient] : Starting StateChangeDispatcher
[org.apache.zookeeper.ZooKeeper] : Client environment:zookeeper.version=3.4.3-1240972, built on 02/06/2012 10:48 GMT
[org.apache.zookeeper.ZooKeeper] : Client environment:host.name=192.168.56.1
[org.apache.zookeeper.ZooKeeper] : Client environment:java.version=1.7.0_80
[org.apache.zookeeper.ZooKeeper] : Client environment:java.vendor=Oracle Corporation
[org.apache.zookeeper.ZooKeeper] : Client environment:java.home=/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/jre
[org.apache.zookeeper.ZooKeeper] : Client environment:java.class.path=mq-client.jar
[org.apache.zookeeper.ZooKeeper] : Client environment:java.library.path=/Users/maping/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
[org.apache.zookeeper.ZooKeeper] : Client environment:java.io.tmpdir=/var/folders/3w/hlhsjmns5m9g6xv6xqk2864h0000gn/T/
[org.apache.zookeeper.ZooKeeper] : Client environment:java.compiler=
[org.apache.zookeeper.ZooKeeper] : Client environment:os.name=Mac OS X
[org.apache.zookeeper.ZooKeeper] : Client environment:os.arch=x86_64
[org.apache.zookeeper.ZooKeeper] : Client environment:os.version=10.10.5
[org.apache.zookeeper.ZooKeeper] : Client environment:user.name=maping
[org.apache.zookeeper.ZooKeeper] : Client environment:user.home=/Users/maping
[org.apache.zookeeper.ZooKeeper] : Client environment:user.dir=/Users/maping/Redhat/amq/demo/jboss-a-mq-6.0.0.redhat-024/extras
[org.apache.zookeeper.ZooKeeper] : Initiating client connection, connectString=localhost:2181 sessionTimeout=10000 watcher=org.fusesource.fabric.zookeeper.internal.ZKClient@51a669d5
[org.apache.zookeeper.ClientCnxn] : Opening socket connection to server /fe80:0:0:0:0:0:0:1%1:2181
[org.apache.zookeeper.client.ZooKeeperSaslClient] : Client will not SASL-authenticate because the default JAAS configuration section 'Client' could not be found. If you are not using SASL, you may ignore this. On the other hand, if you expected SASL to work, please fix your JAAS configuration.
[org.apache.zookeeper.ClientCnxn] : Socket connection established to fe80:0:0:0:0:0:0:1%1/fe80:0:0:0:0:0:0:1%1:2181, initiating session
[org.apache.zookeeper.ClientCnxn] : Session establishment complete on server fe80:0:0:0:0:0:0:1%1/fe80:0:0:0:0:0:0:1%1:2181, sessionid = 0x150656962ea000a, negotiated timeout = 10000
[org.apache.activemq.transport.discovery.DiscoveryTransport] : Adding new broker connection URL: tcp://MaPingdeMacBook-Pro.local:55617
[org.apache.activemq.transport.failover.FailoverTransport] : Successfully connected to tcp://MaPingdeMacBook-Pro.local:55617
Waiting for: 10 messages
[org.fusesource.mq.ConsumerThread] : Received test message: 0
[org.fusesource.mq.ConsumerThread] : Received test message: 1
[org.fusesource.mq.ConsumerThread] : Received test message: 2
[org.fusesource.mq.ConsumerThread] : Received test message: 3
[org.fusesource.mq.ConsumerThread] : Received test message: 4
[org.fusesource.mq.ConsumerThread] : Received test message: 5
[org.fusesource.mq.ConsumerThread] : Received test message: 6
[org.fusesource.mq.ConsumerThread] : Received test message: 7
[org.fusesource.mq.ConsumerThread] : Received test message: 8
[org.fusesource.mq.ConsumerThread] : Received test message: 9
[org.fusesource.mq.ConsumerThread] : Consumer thread finished
Consumed: 10 messages
[org.fusesource.mq.ActiveMQService] : Closed JMS connection
[org.fusesource.fabric.zookeeper.internal.AbstractZKClient] : StateChangeDispatcher terminated.
[org.apache.activemq.transport.failover.FailoverTransport] : Transport (tcp://192.168.56.1:55617) failed, reason:  java.io.EOFException, attempting to automatically reconnect
[org.apache.zookeeper.ZooKeeper] : Session: 0x150656962ea000a closed
[org.apache.zookeeper.ClientCnxn] : EventThread shut down
[org.apache.activemq.transport.failover.FailoverTransport] : Successfully reconnected to tcp://MaPingdeMacBook-Pro.local:55617

小结:Producer 发送消息到 A-MQ-East broker,Consumer 从 A-MQ-West broker 上接收该消息。

AMQ_003:A-MQ 功能演示3:Master/Slave Failover 高可用架构

环境:JBoss A-MQ 6.0.0

1. Master/Slave Failover
Master/Slave Failover是一种高可用解决方案,其实现的重要原理就是 Master/Slave 节点必须指向同一个 persistence store。
A-MQ 使用排它锁来访问 persistence store,先拿到这个锁的节点就是 Master 节点,其它节点是 Slave 节点,Slave 节点不断轮询以获得排它锁,在获得锁之前,它不会发起真正的连接。
一条消息在某一时刻只能存在于一个节点上,当消息所在的节点宕掉时,消息的持久化信息依然安全地保存在persistence store 中,等待其它正常的节点将其获取并发送。
除了使用同一个 persistence store(SAN)来实现Master/Slave Failover这种方式之外,还可以使用RHEL HA以及数据库复制方式实现。

(1)创建Master/Slave container
mq-create --create-container broker1,broker2 tolerant
输出如下:
MQ profile tolerant ready
Jmx Login for root: admin
Jmx Password for root:
Successfully created container broker1
Successfully created container broker2

(2)访问Fabric Management Console,查看tolerant Profile具体配置信息
找到 org.fusesource.mq.fabric.server-tolerant.properties,内容如下:

#Wed Oct 14 08:46:10 CST 2015
connectors=openwire
broker-name=tolerant
config=zk\:/fabric/configs/versions/1.0/profiles/mq-base/broker.xml
data=/Users/maping/Redhat/amq/demo/jboss-a-mq-6.0.0.redhat-024/data/tolerant
group=default
standby.pool=default

(3)生产者
java -jar mq-client.jar producer --brokerUrl discovery:fabric:default --user admin --password admin --destination queue://summit.test --count 10
brokerUrl 值为 discovery:fabric:default,是让 JBoss A-MQ 客户端使用自动发现机制去查找 ActiveMQ 节点的连接信息。
因为Fabric registry 在本地(就是本机),所以不需要提供 Dzookeeper.url=<host>:<port>,如果Fabric registry 不在本地,那么需要提供这个参数。
输出如下:
Using destination: queue://summit.test, on broker: discovery:fabric:default
[org.fusesource.mq.fabric.FabricDiscoveryAgent] : Using local ZKClient
[org.fusesource.fabric.zookeeper.internal.AbstractZKClient] : Starting StateChangeDispatcher
[org.apache.zookeeper.ZooKeeper] : Client environment:zookeeper.version=3.4.3-1240972, built on 02/06/2012 10:48 GMT
[org.apache.zookeeper.ZooKeeper] : Client environment:host.name=192.168.56.1
[org.apache.zookeeper.ZooKeeper] : Client environment:java.version=1.7.0_80
[org.apache.zookeeper.ZooKeeper] : Client environment:java.vendor=Oracle Corporation
[org.apache.zookeeper.ZooKeeper] : Client environment:java.home=/Library/Java/JavaVirtualMachines/jdk1.7.0_80.jdk/Contents/Home/jre
[org.apache.zookeeper.ZooKeeper] : Client environment:java.class.path=mq-client.jar
[org.apache.zookeeper.ZooKeeper] : Client environment:java.library.path=/Users/maping/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.
[org.apache.zookeeper.ZooKeeper] : Client environment:java.io.tmpdir=/var/folders/3w/hlhsjmns5m9g6xv6xqk2864h0000gn/T/
[org.apache.zookeeper.ZooKeeper] : Client environment:java.compiler=
[org.apache.zookeeper.ZooKeeper] : Client environment:os.name=Mac OS X
[org.apache.zookeeper.ZooKeeper] : Client environment:os.arch=x86_64
[org.apache.zookeeper.ZooKeeper] : Client environment:os.version=10.10.5
[org.apache.zookeeper.ZooKeeper] : Client environment:user.name=maping
[org.apache.zookeeper.ZooKeeper] : Client environment:user.home=/Users/maping
[org.apache.zookeeper.ZooKeeper] : Client environment:user.dir=/Users/maping/Redhat/amq/demo/jboss-a-mq-6.0.0.redhat-024/extras
[org.apache.zookeeper.ZooKeeper] : Initiating client connection, connectString=localhost:2181 sessionTimeout=10000 watcher=org.fusesource.fabric.zookeeper.internal.ZKClient@7fe7f581
[org.apache.zookeeper.ClientCnxn] : Opening socket connection to server /fe80:0:0:0:0:0:0:1%1:2181
[org.apache.zookeeper.client.ZooKeeperSaslClient] : Client will not SASL-authenticate because the default JAAS configuration section 'Client' could not be found. If you are not using SASL, you may ignore this. On the other hand, if you expected SASL to work, please fix your JAAS configuration.
[org.apache.zookeeper.ClientCnxn] : Socket connection established to fe80:0:0:0:0:0:0:1%1/fe80:0:0:0:0:0:0:1%1:2181, initiating session
[org.apache.zookeeper.ClientCnxn] : Session establishment complete on server fe80:0:0:0:0:0:0:1%1/fe80:0:0:0:0:0:0:1%1:2181, sessionid = 0x150656962ea0003, negotiated timeout = 10000
[org.apache.activemq.transport.discovery.DiscoveryTransport] : Adding new broker connection URL: tcp://MaPingdeMacBook-Pro.local:55469
[org.apache.activemq.transport.failover.FailoverTransport] : Successfully connected to tcp://MaPingdeMacBook-Pro.local:55469
[org.fusesource.mq.ProducerThread] : Sent: test message: 0
[org.fusesource.mq.ProducerThread] : Sent: test message: 1
[org.fusesource.mq.ProducerThread] : Sent: test message: 2
[org.fusesource.mq.ProducerThread] : Sent: test message: 3
[org.fusesource.mq.ProducerThread] : Sent: test message: 4
[org.fusesource.mq.ProducerThread] : Sent: test message: 5
[org.fusesource.mq.ProducerThread] : Sent: test message: 6
[org.fusesource.mq.ProducerThread] : Sent: test message: 7
[org.fusesource.mq.ProducerThread] : Sent: test message: 8
[org.fusesource.mq.ProducerThread] : Sent: test message: 9
[org.fusesource.mq.ProducerThread] : Producer thread finished
Produced: 10
[org.fusesource.mq.ActiveMQService] : Closed JMS connection
[org.fusesource.fabric.zookeeper.internal.AbstractZKClient] : StateChangeDispatcher terminated.
[org.apache.zookeeper.ZooKeeper[] : Session: 0x150656962ea0003 closed
org.apache.zookeeper.ClientCnxn] : EventThread shut down
[org.apache.activemq.transport.failover.FailoverTransport] : Transport (tcp://192.168.56.1:55469) failed, reason:  java.io.EOFException, attempting to automatically reconnect
[org.apache.activemq.transport.failover.FailoverTransport] : Successfully reconnected to tcp://MaPingdeMacBook-Pro.local:55469

(3)查看fabric集群情况

fabric:cluster-list
输出如下:
[cluster]                      [masters]                      [slaves]                       [services]
stats/default                                                                               
fusemq/default                                                                              
   tolerant                    broker1                        broker2                        tcp://MaPingdeMacBook-Pro.local:55469

(4)生产者,增加发送消息数量以及发送间隔
java -jar mq-client.jar producer --brokerUrl discovery:fabric:default --user admin --password admin --destination queue://summit.test --count 1000 -—sleep 1000

在此期间,启动/停止 container:fabric:container-{start,stop} <container name>

fabric:container-stop broker1
观察fabric集群的变化:fabric:cluster-list
fabric:container-start broker1
观察fabric集群的变化:fabric:cluster-list
fabric:container-stop broker2
观察fabric集群的变化:fabric:cluster-list
fabric:container-start broker2
观察fabric集群的变化:fabric:cluster-list