前言
当前码农界言必称微服务,行必说分布式。。。嗯,让我也蹭蹭热点,也写写“微服务”。
新建这个程序的本意其实是,作为服务提供者,每次都拖着一个tomcat运行似乎不太合适,所以弄成一个可独立发布的java application程序。也算是简化一下项目了。
背景
这次的项目依然是spring+gradle+xxl-conf配置中心。
项目结构如下:
创建一个gradle的java模块过程不累述,下面看看当前子模块的build.gradle:
plugins {
id 'java'
}
group 'net.w2p'
version '1.0-SNAPSHOT'
dependencies {
compile project(":Shared")
compile (project(":xxl-conf-core"))
compile (project(":WebExt"))
//--redis
compile ref4RedisClient
//【spring 框架】
compile ref4SpringFramework
//【mybatis】
compile ref4MyBatis
compile ref4MybatisSpring
//【apache commons】
compile ref4ApacheCommons
//postgresql
compile ref4PostgresqlJdbcDriver
//druid
compile ref4Druid
}
暂时只需要这么多依赖,以后随项目而添加。
编译替换配置文件
执行:
结果为:
替换复制配置成功。
spring的配置文件说明
spring的配置文件有两个,一个是applicationContext.xml,一个是spring-mvc.xml,下面进行要点说明:
注意,java application程序中不需要spring-mvc.xml,因为spring-mvc.xml是web专用的。applicationContext存放在resource下面的,假如没有的话请新建:
applicationContext.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd"
>
<!--<!–导入xxl conf spring配置文件–>-->
<!--<import resource="classpath:/spring/applicationContext-XxlConf.xml"></import>-->
<!--注意,该上下文配置文件只用于 contextLoaderListener,是程序基本上下文,作为父context被其他上下文引用-->
<!--分工:整个Web应用程序需要共享的一些组件,比如DAO,数据库的ConnectionFactory等,mybatis,等等-->
<!--
net.w2p.local.Shared.mybatis.TypeHandlers =》 自定义mybatis数据类型转换,譬如,数组类型,布尔值类型等。
net.w2p.local.plugins.BeanConfiguration=》自定义的java类作为configuration的方法,包含了数据库datasource源
-->
<context:component-scan
base-package="
net.w2p.Shared.mybatis.TypeHandlers,
net.w2p.local.plugins.BeanConfiguration
"/>
</beans>
入口程序编写配置说明
入口程序在:
代码为:
package net.w2p.BaseServiceApp;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.concurrent.Semaphore;
public class Application {
public static void main(String[] args){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
context.start();
//--用信号量阻塞主线程。
Semaphore stop=new Semaphore(0);
try{
stop.acquire();
}
catch (Exception ed){
ed.printStackTrace();
}
System.out.println("服务关闭");
}
}
配置xxl-conf
配置过程其实跟web模块的一样的,直接抄即可。
编译配置文件
首先,请执行:
gradle -q compileConfig -Denv=test
先执行命令生成xxl-conf的配置properties文件:
生成的配置文件内容如下:
然后,在FileServerWebApp项目下面的BeanConfiguration添加xxl-conf的配置以及初始化:
代码如下:
package net.w2p.local.plugins.BeanConfiguration;
import com.xxl.conf.core.spring.XxlConfFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
/***配置中心配置***/
@Configuration
public class ConfCenter {
/***xxl-conf配置工厂***/
@Bean(name="xxlConfFactory")
public XxlConfFactory xxlConfFactory(){
/****
* 读取本地配置文件
* ***/
Properties config=new Properties();
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("conf/xxl-conf.properties");
try {
InputStreamReader is = new InputStreamReader(in, "utf-8");
config.load(is);
is.close();
in.close();
}
catch (Exception ed){
ed.printStackTrace();
}
String adminAddress=config.getProperty("xxl.conf.admin.address");
String env=config.getProperty("xxl.conf.env");
String accessToken=config.getProperty("xxl.conf.access.token");
String mirrorfile=config.getProperty("xxl.conf.mirrorfile");
XxlConfFactory xxlConf = new XxlConfFactory();
xxlConf.setAdminAddress(adminAddress);
xxlConf.setEnv(env);
xxlConf.setAccessToken(accessToken);
xxlConf.setMirrorfile(mirrorfile);
return xxlConf;
}
}
java application程序的测试
ps!!!java application的spring的测试与web的spring测试大部分一样,但是只有一点要记住,不能应用@WebAppConfiguration属性否则会报错:
错误代码如下:
org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.test.context.web.WebDelegatingSmartContextLoader]: Constructor threw exception; nested exception is java.lang.NoClassDefFoundError: javax/servlet/ServletContext
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:184)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:122)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:152)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.resolveContextLoader(AbstractTestContextBootstrapper.java:457)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:348)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildMergedContextConfiguration(AbstractTestContextBootstrapper.java:294)
at org.springframework.test.context.support.AbstractTestContextBootstrapper.buildTestContext(AbstractTestContextBootstrapper.java:108)
意思是无法初始化webContext----额,java application不是web。。。
好了,下面进行配置测试:
首先,新建一个测试基础类,以后的测试都要继承自这个基础类进行测试,譬如,就叫BaseTest
内容如下:
package main;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
/***注意,跟spring+web整合的测试不同点在于:
* 1、没有WebAppConfiguration
* 2、没有spring-mvc.xml这个配置文件
*
* ***/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={
"classpath*:applicationContext.xml"
}
)
public class BaseTest {
Date beginDate;
Long begin=null;
Long end=null;
Date endDate=null;
@Before
public void init() {
//--初始化spring上下文
System.out.println("初始化spring上下文中......");
//在运行测试之前的业务代码
beginDate = new Date();
begin = beginDate.getTime();
System.out.println("任务开始时间:" + beginDate);
}
@After
public void after() {
//在测试完成之后的业务代码
endDate = new Date();
end = endDate.getTime();
System.out.println("任务结束时间:" + endDate + "");
System.out.println("任务话费时间:" + (end - begin) + "毫秒");
}
/***用于测试最大并发***/
public void runMultiThreadTest(int totalThreadCount, int threadPoolSize, Runnable runnable){
{
final int threadSize= totalThreadCount;
ExecutorService executor= Executors.newFixedThreadPool(threadPoolSize);
final AtomicInteger lockCount=new AtomicInteger(threadSize);
final AtomicInteger successCount=new AtomicInteger(0);
try {
for (int i = 0; i < threadSize; i++) {
final int theThreadNumber = i;
executor.submit(new Runnable() {
@Override
public void run() {
try {
runnable.run();
successCount.incrementAndGet();
}
catch (Exception ed){
ed.printStackTrace();
}
finally {
lockCount.decrementAndGet();
}
}
});
}
while(true){
synchronized (this){
if(lockCount.intValue()>0){
;
}
else{
break;
}
}
}
System.out.println("注意当前线程池最大线程数量为"+threadPoolSize+"个");
System.out.println("共运行线程"+threadSize+"个,成功运行线程:"+successCount.get()+"个");
}
catch (Exception ed){
ed.printStackTrace();
}
}
}
/***用于测试任务阻塞的任务队列执行下的性能***/
public void runMultiThreadByBlockQueue(int threadCount,TaskProducer producer,TaskConsumer consumer){
final LinkedBlockingQueue<TaskOutLine> queue=new LinkedBlockingQueue<>(threadCount);
final int threadSize=threadCount;
ExecutorService executor= Executors.newFixedThreadPool(threadSize);
final AtomicInteger lockCount=new AtomicInteger(threadSize);
final AtomicInteger successCount=new AtomicInteger(0);
try {
/***线程池同时产生任务队列***/
for (int i = 0; i < threadSize; i++) {
final int theThreadNumber = i;
TaskOutLine tmpOutLine=new TaskOutLine();
tmpOutLine.taskIndex=theThreadNumber;
executor.submit(new Runnable() {
@Override
public void run() {
try {
tmpOutLine.taskData=producer.produce();
queue.put(tmpOutLine);
}
catch (Exception ed){
ed.printStackTrace();
}
finally {
}
}
});
}
/***另起一个线程用于消费队列**/
new Thread(new Runnable() {
@Override
public void run() {
while (lockCount.get()>0){
try{
TaskOutLine currentObj=queue.take();
consumer.consume(currentObj);
successCount.incrementAndGet();
}
catch (Exception ed){
}
finally {
lockCount.decrementAndGet();
}
}
}
}).start();
while(true){
synchronized (this){
if(lockCount.intValue()>0){
;
}
else{
break;
}
}
}
System.out.println("共运行线程"+threadSize+"个,成功运行线程:"+successCount.get()+"个");
}
catch (Exception ed){
ed.printStackTrace();
}
}
public interface TaskProducer{
public Object produce();
}
public interface TaskConsumer{
public void consume(TaskOutLine taskOutLine);
}
public class TaskOutLine{
public int taskIndex=0;
public Object taskData=new Object();
public int getTaskIndex() {
return taskIndex;
}
public void setTaskIndex(int taskIndex) {
this.taskIndex = taskIndex;
}
public Object getTaskData() {
return taskData;
}
public void setTaskData(Object taskData) {
this.taskData = taskData;
}
}
}
测试xxl-conf配置情况
编写测试代码:
代码为:
package other;
import com.xxl.conf.core.XxlConfClient;
import main.BaseTest;
import net.w2p.WebExt.Plugins.RedisPlugin;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import java.util.Date;
public class TestCase1 extends BaseTest {
@Test
public void printConf(){
String str= XxlConfClient.get("default.key01", null);
System.out.println("从配置中心获取default.key01的值是:"+str);
}
}
测试结果:
测试完成。
注意,假如运行application的话,会看到:
程序一直阻塞,那是因为信号量一直阻塞主线程了。