1. 问题描述
(资料图片)
我们的项目是基于端到端通信的。分为客户端、服务端。项目大致分为5个模块。 server、client、core、common以及test模块。 显而易见,server是服务端模块,client是客户端模块,core是项目核心数据结构模块,common是公共类模块(比如一些工具类),test模块从各个子项目抽出来的作为一个单独的test模块。 项目地址:DistKV Project 欢迎star、fork以及contributing。现在的问题是:需要在test模块中测试跟服务端的通信,看得到的结果是否符合预期。 最简单的做法就是:在测试的时候,我们在server模块,通过IEDA点击绿色的Run按钮,就可以启动服务端了,然后在test模块运行测试代码,完成了测试之后,我们再去server模块Stop掉服务端。 这样做存在的问题在于:每次测试都要Run一下服务端的代码,不麻烦? 而且我们运行mvn test命令的时候,应该不需要关心server模块的东西。这称为"解耦合"?
2. 解决办法
我们先考虑在命令行中应该怎么去跑一个程序。简单的Hello World大家都会跑,无非就是先用javac编译,再java运行。但是Hello World不需要依赖其他jar包,所以这样是可行的。 而我们的服务端DstRpcServer.java文件里面依赖了很多第三方包,所以我们单纯的进入到server模块的DstRpcServer.java文件所在路径,执行下面两条语句是不行的。 javac DstRpcServer.java以及 java DstRpcServer原因有两个:
第一个是依赖很多第三方jar包。第二个是因为该路径不是classpath。
注:maven项目的classpath一般在模块下的target/classes下面。 因此,如果我们要在命令行中运行这个DstRpcServer.java,应该怎么运行? 第一步:需要将相关依赖打包成一个jar包,设置DstRpcServer为入口主类。此阶段只要借助以下插件完成。
maven-assembly-pluginjar-with-dependenciesorg.dst.server.service.DstRpcServermake-assemblypackagesingle
第二步:运行 java -classpath C:\Users\Administrator\Desktop\gitProject\dst\server\target\dst-server-1.0-SNAPSHOT-jar-with-dependencies.jar org.dst.server.service.DstRpcServer此处的classpath是我本地项目的路径。类名应该是完整类名,即包名+类名。 但我们怎么在执行mvn test的时候让其执行呢。 完整解决办法:我们在test模块中,利用ProcessBuilder类去新创建一个进程去执行这个命令。执行完之后再销毁该进程。 需要注意的点是:我们需要根据每个人电脑的上的项目路径来动态获取classpath。
public class TestUtil { private static final Logger LOGGER = LoggerFactory.getLogger(TestUtil.class); //get the relative path private static final String SUFFIX_JAR_DIR = "server" + File.separator + "target" + File.separator + "dst-server-1.0-SNAPSHOT-jar-with-dependencies.jar"; private static Process rpcServerProcess = null; /** * @param command the command to start rpc server using a new process. */ private static void executeCommand(Listcommand) { try { LOGGER.debug("Executing command: {}", String.join(" ", command)); ProcessBuilder processBuilder = new ProcessBuilder(command).redirectOutput(ProcessBuilder.Redirect.INHERIT) .redirectError(ProcessBuilder.Redirect.INHERIT); rpcServerProcess = processBuilder.start(); // TODO(qwang): Refine this wait rpcServerProcess.waitFor(1, TimeUnit.SECONDS); } catch (Exception e) { rpcServerProcess.destroy(); throw new RuntimeException("Error executing command " + String.join(" ", command), e); } } public static void startRpcServer() { final File userDir = new File(System.getProperty("user.dir")); // get the absolute path final String jarDir = userDir.getParent() + File.separator + SUFFIX_JAR_DIR; final ListstartCommand = ImmutableList.of( "java", "-classpath", jarDir, "org.dst.server.service.DstRpcServer" ); executeCommand(startCommand); } public static void stopRpcServer() { rpcServerProcess.destroy(); }}
然后在测试代码中调用该类的方法。
public class DstRpcServerTest { @Test public void testRpcServer() { //start the server TestUtil.startRpcServer(); RpcClientOptions options = new RpcClientOptions(); options.setProtocolType(Options.ProtocolType.PROTOCOL_BAIDU_STD_VALUE); options.setWriteTimeoutMillis(1000); options.setReadTimeoutMillis(1000); options.setMaxTotalConnections(1000); options.setMinIdleConnections(10); String url = "list://127.0.0.1:8082"; RpcClient client = new RpcClient(url, options); DstStringService stringService = BrpcProxy.getProxy(client, DstStringService.class); // Test string put request DstServerProtocol.StringPutRequest stringPutRequest = DstServerProtocol.StringPutRequest.newBuilder() .setKey("k1") .setValue("v1") .build(); DstServerProtocol.StringPutResponse stringResponse = stringService.strPut(stringPutRequest); Assertions.assertEquals("ok", stringResponse.getResult()); // Test string get request DstServerProtocol.StringGetRequest strGetRequest = DstServerProtocol.StringGetRequest.newBuilder() .setKey("k1") .build(); DstServerProtocol.StringGetResponse stringGetRequest = stringService.strGet(strGetRequest); Assertions.assertEquals("v1", stringGetRequest.getResult()); client.stop();// stop the server TestUtil.stopRpcServer(); }}