dubbo-利用assembly插件和内嵌tomcat闭环应用

前言

用过dubbo的同学应该知道,dubbo既可以通过war包发布到 tomcat中提供dubbo服务,也可以通过dubbo容器机制直接运行,提供服务提供者。关于dubbo容器机制的探讨,可以参考之前写的一篇文章 《dubbo入门-容器(可执行性jar启动项目)》 。如果使用war包部署到tomcat这种方式,部署dubbo提供者服务 ,其实是相当耗资源的,因为dubbo本来就通过spring容器使用了单应用的功能,所以对于服务提供者,还是推荐使用内置container的方式部署服务。然而,在使用可执行jar包这个方案中,发现修改jar包里面配置文件特别麻烦,除此之外,对于服务消费方的工程,仍要复制war包到tomcat工作目录,再到tomcat执行文件夹中启动tomcat。

最近看了开涛的 《是时候闭环Java应用了》 文章,然后动手给基于dubbo的web服务和service服务做了一个闭环的demo。在这里分享以下。

该demo实现了以下功能:

  1. 通过maven assembly插件按约定结构打包工程
  2. 非web的dubbo服务提供者项目使用dubbo内置的spring容器启动
  3. web的dubbo消费者项目使用内嵌tomcat方式启动
  4. 支持脚本方式或IDE直接运行的方式启动

实现

DEMO的模块依赖关系如下,其中assembly-service是dubbo的提供者,assembly-web是dubbo的消费者,assembly-common为服务提供者和消费者提供共同依赖。

输入图片说明

输入图片说明

非web工程

打包后的结构如下:

输入图片说明

实现以上打包结构需要借助maven的profiles特性,maven的resources插件选择性拷贝资源文件,assembly插件打包输出。

** 清单:pom **

<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/maven-v4_0_0.xsd">
    <parent>
        <artifactId>assembly</artifactId>
        <groupId>com.github.thinwonton</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>assembly-service</artifactId>
    <name>assembly-service</name>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>

    <profiles>
        <profile>
            <!-- 本地开发环境 -->
            <id>dev</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>

            <properties>
                <profiles.active>dev</profiles.active>
            </properties>
        </profile>
        <profile>
            <!-- 测试环境 -->
            <id>test</id>
            <properties>
                <profiles.active>test</profiles.active>
            </properties>
        </profile>
        <profile>
            <!-- 生产环境 -->
            <id>prod</id>
            <properties>
                <profiles.active>prod</profiles.active>
            </properties>
        </profile>
    </profiles>

    <dependencies>

          <!-- 忽略依赖 -->

    </dependencies>

    <build>
        <finalName>assembly-service</finalName>

        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <!-- 资源根目录排除各环境的配置,使用单独的资源目录来指定 -->
                <excludes>
                    <exclude>test/**</exclude>
                    <exclude>prod/**</exclude>
                    <exclude>dev/**</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources/${profiles.active}</directory>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptor>src/assembly/assembly.xml</descriptor>
                    <finalName>${project.build.finalName}</finalName>
                    <!--是否在生成的打包文件的文件名中包含assembly id-->
                    <appendAssemblyId>false</appendAssemblyId>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>directory</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

** 清单:assembly.xml **

<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3
                                http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <id>package</id>
    <!--打包格式,此处使用的是dir,还可以是zip、rar等-->
    <formats>
        <format>dir</format>
    </formats>

    <includeBaseDirectory>false</includeBaseDirectory>

    <fileSets>
        <!-- 可执行文件 -->
        <fileSet>
            <directory>src/bin</directory>
            <outputDirectory>bin</outputDirectory>
            <includes>
                <include>*.bat</include>
            </includes>
            <lineEnding>dos</lineEnding>
        </fileSet>
        <fileSet>
            <directory>src/bin</directory>
            <outputDirectory>bin</outputDirectory>
            <includes>
                <include>*.sh</include>
            </includes>
            <lineEnding>unix</lineEnding>
            <!--文件权限-->
            <fileMode>0755</fileMode>
        </fileSet>

        <!-- classes -->
        <fileSet>
            <directory>${project.build.directory}/classes</directory>
            <outputDirectory>classes</outputDirectory>
        </fileSet>
    </fileSets>

    <!-- 依赖jar包,拷贝到lib目录 -->
    <dependencySets>
        <dependencySet>
            <outputDirectory>lib</outputDirectory>
            <excludes>
                <!--排除当前jar包-->
                <exclude>${project.groupId}:${project.artifactId}</exclude>
            </excludes>
        </dependencySet>
    </dependencySets>
</assembly>

** 启动类 **

package com.github.thinwonton.assembly.startup;

public class Bootstrap {
    public static void main(String[] args) {
        for (String arg : args) {
            System.out.println("-----------" + arg);
        }
        com.alibaba.dubbo.container.Main.main(args);
    }
}

启动服务,有两种方式可以运行:(1)可以直接在IDE中右键运行启动服务,需要加入启动参数 -Ddubbo.spring.config=classpath:/spring/applicationContext-config.xml,告知dubbo在哪里找到spring配置文件;(2)使用脚本运行服务,使用java命令,需要指定classpath路径和Main方法类。

** 清单:start.bat **

@echo off

echo ---------------------------
echo starting server...
echo ---------------------------

rem 保存当前路径
set "BIN_DIR_PATH=%cd%"

rem 获取上一级路径
cd..
set "CODE_HOME=%cd%"

rem 恢复路径
cd %BIN_DIR_PATH%

echo %CODE_HOME%

rem 类路径
set CLASSPATH="%CODE_HOME%/classes;%CODE_HOME%/lib/*"

rem 主运行程序类路径

set MAIN_CLASS="com.github.thinwonton.assembly.startup.Bootstrap"

rem dubbo加载spring容器的路径
set DUBBO_SPRING_CONFIG="-Ddubbo.spring.config=classpath:/spring/applicationContext-config.xml"

rem jvm参数
set JAVA_OPTS=-server -Xms128m -Xmx256m -Xss256k -XX:MaxDirectMemorySize=128m

rem java可执行文件位置
set _EXECJAVA="%JAVA_HOME%/bin/java"

%_EXECJAVA% -Dfile.encoding=utf8 %DUBBO_SPRING_CONFIG% %JAVA_OPTS% -classpath %CLASSPATH% %MAIN_CLASS%

** 清单:start.sh**

#!/bin/sh
echo -------------------------------------------
echo start server
echo -------------------------------------------

# bin路径
cd `dirname $0`
BIN_DIR=`pwd`

# 设置项目代码路径
cd ..
export CODE_HOME=`pwd`

#日志路径
export LOG_PATH="$CODE_HOME/logs"
mkdir -p $LOG_PATH

# 设置依赖路径
export CLASSPATH="$CODE_HOME/WEB-INF/classes:$CODE_HOME/WEB-INF/lib/*"

# java可执行文件位置
export _EXECJAVA="$JAVA_HOME/bin/java"

# JVM启动参数
export JAVA_OPTS="-server -Xms128m -Xmx256m -Xss256k-XX:MaxDirectMemorySize=128m"

# 服务端端口、上下文、项目根配置
export SERVER_INFO="-Dserver.port=8090 -Dserver.contextPath= -Dserver.docRelativePath=../"

# 启动类
export MAIN_CLASS="com.github.thinwonton.assembly.startup.Bootstrap"

$_EXECJAVA $JAVA_OPTS -classpath $CLASSPATH $SERVER_INFO $MAIN_CLASS &
tail -f $LOG_PATH/stdout.log

** 清单:stop.sh**

#!/bin/sh

# 设置项目代码路径
cd ..
export CODE_HOME=`pwd`

#日志路径
export LOG_PATH="$CODE_HOME/logs"
mkdir -p $LOG_PATH

# 启动类
export MAIN_CLASS="com.github.thinwonton.assembly.startup.Bootstrap"

echo -------------------------------------------
echo stop server

#所有相关进程
PIDs=`jps -l | grep $MAIN_CLASS | awk '{print $1}'`
#停止进程
if [ -n "$PIDs" ]; then
  for PID in $PIDs; do
      kill $PID
      echo "kill $PID"
  done
fi

#等待50秒
for i in 1 10; do
  PIDs=`jps -l | grep $MAIN_CLASS | awk '{print $1}'`
  if [ ! -n "$PIDs" ]; then
    echo "stop server success"
    echo -------------------------------------------
    break
  fi
  echo "sleep 5s"
  sleep 5
done

#如果等待50秒还没有停止完,直接杀掉
PIDs=`jps -l | grep $MAIN_CLASS | awk '{print $1}'`
if [ -n "$PIDs" ]; then
  for PID in $PIDs; do
      kill -9 $PID
      echo "kill -9 $PID"
  done
fi
tail -fn200 $LOG_PATH/stdout.log

web工程

web工程使用嵌入tomcat的方式启动,所以需要引入tomcat的依赖以及在启动类中自行启动tomcat

打包后的结构如下:

输入图片说明

清单pom

<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/maven-v4_0_0.xsd">
    <parent>
        <artifactId>assembly</artifactId>
        <groupId>com.github.thinwonton</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <name>assembly-web</name>
    <artifactId>assembly-web</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>

    <profiles>
        <profile>
            <!-- 本地开发环境 -->
            <id>dev</id>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>

            <properties>
                <profiles.active>dev</profiles.active>
            </properties>
        </profile>
        <profile>
            <!-- 测试环境 -->
            <id>test</id>
            <properties>
                <profiles.active>test</profiles.active>
            </properties>
        </profile>
        <profile>
            <!-- 生产环境 -->
            <id>prod</id>
            <properties>
                <profiles.active>prod</profiles.active>
            </properties>
        </profile>
    </profiles>

    <dependencies>

      <!-- 忽略依赖 -->

    </dependencies>

    <build>
        <finalName>assembly-web</finalName>

        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <!-- 资源根目录排除各环境的配置,使用单独的资源目录来指定 -->
                <excludes>
                    <exclude>test/**</exclude>
                    <exclude>prod/**</exclude>
                    <exclude>dev/**</exclude>
                </excludes>
            </resource>
            <resource>
                <directory>src/main/resources/${profiles.active}</directory>
            </resource>
        </resources>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptor>src/assembly/assembly.xml</descriptor>
                    <finalName>${project.build.finalName}</finalName>
                    <!--是否在生成的打包文件的文件名中包含assembly id-->
                    <appendAssemblyId>false</appendAssemblyId>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>directory</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>

    </build>

</project>

清单:assembly.xml

<?xml version="1.0" encoding="UTF-8"?>
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3
                                http://maven.apache.org/xsd/assembly-1.1.3.xsd">
    <id>package</id>
    <!--打包格式,此处使用的是dir,还可以是zip、rar等-->
    <formats>
        <format>dir</format>
    </formats>

    <includeBaseDirectory>false</includeBaseDirectory>

    <fileSets>
        <!-- 可执行文件 -->
        <fileSet>
            <directory>src/bin</directory>
            <outputDirectory>bin</outputDirectory>
            <includes>
                <include>*.bat</include>
            </includes>
            <lineEnding>dos</lineEnding>
        </fileSet>
        <fileSet>
            <directory>src/bin</directory>
            <outputDirectory>bin</outputDirectory>
            <includes>
                <include>*.sh</include>
            </includes>
            <lineEnding>unix</lineEnding>
            <!--文件权限-->
            <fileMode>0755</fileMode>
        </fileSet>
    </fileSets>
</assembly>

** 启动类 TomcatBootstrap.java **。启动参数server.docPath指定web资源的目录,如果没有启动参数server.docPath,会查找工程目录中的src/main/webapp目录,为了实现在IDE中直接run,如果有启动参数,会把该路径传给TOMCAT,让它加载web.xml和网页资源。

建议查看tomcat启动类源码org.apache.catalina.startup.Bootstrap,仿照该启动类写。

package com.github.thinwonton.assembly.startup;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.AprLifecycleListener;
import org.apache.catalina.core.JreMemoryLeakPreventionListener;
import org.apache.catalina.core.StandardServer;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.http11.Http11NioProtocol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

/**
 * 启动参数说明:<br/>
 * server.port:tomcat端口 <br/>
 * server.docPath:WEB-INF所在的文件夹路径 <br/>
 * 
 */
public class TomcatBootstrap {
    private static final Logger LOGGER = LoggerFactory.getLogger(TomcatBootstrap.class);

    public static void main(String[] args) throws Exception {

        //提升性能(https://wiki.apache.org/tomcat/HowTo/FasterStartUp)
        System.setProperty("tomcat.util.scan.StandardJarScanFilter.jarsToSkip", "*.jar");
        //System.setProperty("securerandom.source","file:/dev/./urandom");

        int port = Integer.parseInt(System.getProperty("server.port", "8080")); //端口

        String contextPath = System.getProperty("server.contextPath", ""); //上下文路径

        String docPath = System.getProperty("server.docPath", getDefaultDocPath()); //

        LOGGER.info("server port : {}, context path : {},doc path : {}", port, contextPath, docPath);

        Tomcat tomcat = createTomcat(port, contextPath, docPath);
        tomcat.start();
        LOGGER.info("tomcat started");
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    tomcat.stop();
                } catch (LifecycleException e) {
                    LOGGER.error("stoptomcat error.", e);
                }
            }
        });
        tomcat.getServer().await();
    }

    private static String getDefaultDocPath() {
        File projectDir = getProjectDir();
        String path = new StringBuffer().append("src").append(File.separator).append("main").append(File
                .separator).append("webapp").toString(); // "src/main/webapp"
        return new File(projectDir, path).getPath();
    }

    private static File getProjectDir() {
        File classpathDir = getClasspathDir();
        return classpathDir.getParentFile().getParentFile();
    }

    private static File getClasspathDir() {
        return new File(Thread.currentThread().getContextClassLoader().getResource(".").getFile());
    }

    private static Tomcat createTomcat(int port, String contextPath, String docPath) throws Exception {
        Tomcat tomcat = new Tomcat();
        String tmpdir = System.getProperty("java.io.tmpdir");
        tomcat.setBaseDir(tmpdir);
        tomcat.getHost().setAppBase(tmpdir);
        tomcat.getHost().setAutoDeploy(false);
        tomcat.getHost().setDeployOnStartup(false);
        tomcat.getEngine().setBackgroundProcessorDelay(-1);
        tomcat.setConnector(newNioConnector());
        tomcat.getConnector().setPort(port);
        tomcat.getService().addConnector(tomcat.getConnector());
        Context context = tomcat.addWebapp(contextPath, docPath);
        StandardServer server = (StandardServer) tomcat.getServer();
        //APR library loader. Documentation at /docs/apr.html
        server.addLifecycleListener(new AprLifecycleListener());
        //Prevent memory leaks due to use of particularjava/javax APIs
        server.addLifecycleListener(new JreMemoryLeakPreventionListener());
        return tomcat;
    }

    //在这里调整参数优化
    private static Connector newNioConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
        return connector;
    }

}

清单:start.bat

@echo off

rem 保存当前路径
set "BIN_DIR_PATH=%cd%"

rem 获取上一级路径
cd..
set "CODE_HOME=%cd%"

rem 恢复路径
cd %BIN_DIR_PATH%

rem 日志路径
set LOG_PATH="%CODE_HOME%/logs"

echo %CODE_HOME%

rem 设置依赖路径
set CLASSPATH="%CODE_HOME%/WEB-INF/classes;%CODE_HOME%/WEB-INF/lib/*"

rem java可执行文件位置
set _EXECJAVA="%JAVA_HOME%/bin/java"

rem JVM启动参数
set JAVA_OPTS=-server -Xms128m -Xmx256m -Xss256k -XX:MaxDirectMemorySize=128m

rem 服务端端口、上下文、项目根配置
set SERVER_INFO=-Dserver.port=8090 -Dserver.contextPath= -Dserver.docPath=%CODE_HOME%

rem 启动类
set MAIN_CLASS="com.github.thinwonton.assembly.startup.TomcatBootstrap"

%_EXECJAVA% -Dfile.encoding=utf8 %JAVA_OPTS% -classpath %CLASSPATH% %SERVER_INFO% %MAIN_CLASS%

清单:start.sh

#!/bin/sh
echo -------------------------------------------
echo start server
echo -------------------------------------------

# bin路径
cd `dirname $0`
BIN_DIR=`pwd`

# 设置项目代码路径
cd ..
export CODE_HOME=`pwd`

#日志路径
export LOG_PATH="$CODE_HOME/logs"
mkdir -p $LOG_PATH

# 设置依赖路径
export CLASSPATH="$CODE_HOME/WEB-INF/classes:$CODE_HOME/WEB-INF/lib/*"

# java可执行文件位置
export _EXECJAVA="$JAVA_HOME/bin/java"

# JVM启动参数
export JAVA_OPTS="-server -Xms128m -Xmx256m -Xss256k-XX:MaxDirectMemorySize=128m"

# 服务端端口、上下文、项目根配置
export SERVER_INFO="-Dserver.port=8090 -Dserver.contextPath= -Dserver.docRelativePath=../"

# 启动类
export MAIN_CLASS="com.github.thinwonton.assembly.startup.TomcatBootstrap"

$_EXECJAVA $JAVA_OPTS -classpath $CLASSPATH $SERVER_INFO $MAIN_CLASS &
tail -f $LOG_PATH/stdout.log

清单:stop.sh

#!/bin/sh

# 设置项目代码路径
cd ..
export CODE_HOME=`pwd`

#日志路径
export LOG_PATH="$CODE_HOME/logs"
mkdir -p $LOG_PATH

# 启动类
export MAIN_CLASS=com.github.thinwonton.assembly.startup.TomcatBootstrap

echo -------------------------------------------
echo stop server
echo -------------------------------------------

#所有相关进程
PIDs=`jps -l | grep $MAIN_CLASS | awk '{print $1}'`
#停止进程
if [ -n "$PIDs" ]; then
  for PID in $PIDs; do
      kill $PID
      echo "kill $PID"
  done
fi

#等待50秒
for i in 1 10; do
  PIDs=`jps -l | grep $MAIN_CLASS | awk '{print $1}'`
  if [ ! -n "$PIDs" ]; then
    echo "stop server success"
    echo -------------------------------------------
    break
  fi
  echo "sleep 5s"
  sleep 5
done

#如果等待50秒还没有停止完,直接杀掉
PIDs=`jps -l | grep $MAIN_CLASS | awk '{print $1}'`
if [ -n "$PIDs" ]; then
  for PID in $PIDs; do
      kill -9 $PID
      echo "kill -9 $PID"
  done
fi
tail -fn200 $LOG_PATH/stdout.log

相关代码

http://git.oschina.net/thinwonton/dubbo-assembly

参考资料

http://jinnianshilongnian.iteye.com/blog/2317830

转载于:https://my.oschina.net/thinwonton/blog/968195

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值