Nemo

Nemo 关注TA

路漫漫其修远兮,吾将上下而求索。

Nemo

Nemo

关注TA

路漫漫其修远兮,吾将上下而求索。

  •  普罗旺斯
  • 负责帅就完事了
  • 写了1,496,113字

该文章投稿至Nemo社区   Java  板块 复制链接


SpringMVC +Spring+ Ibaties + Dubbo + Zookeeper实现简单分布式架构备份

发布于 2016/11/28 11:31 2,758浏览 2回复 21,188

之前有一个项目上正好需求上要求使用Dubbo + Zookeeper做分布式。

稍稍找了下Dubbo的文档,结合了下网络上的一些资料,简单做了一个整合SpringMVC + Spring + Ibaties + Dubbo + Zookeeper的架构。这里稍稍简单整理记录下这个架构的一些内容,留作备忘。

准备工作:

1、这里需要用到一个服务注册中心,这里使用的是Zookeeper。之前做过一个Ubuntu下安装Zookeeper的备忘,可以参考这里。安装完Zookeeper之后记得务必打开Zookeeper的服务,不然在后面测试的时候会有问题。

2、项目是使用maven做的版本控制。

架构分层:

core//这一层用来存放用到的第三方jar包,后期也可以用来存放一些共用的工具包。

entity //这一层用来存放实体,所有的bean都存放到这里。

api //这一层用来存放消费层和生产者服务层共用的一些接口,因为后期生产者需要将接口暴露给消费者。它需要通过maven依赖entity

server //这一层用来存放所有的服务提供者,所有的service + dao一类的操作都放到这里。它里面也需要提供服务的启动方法。它需要依赖api和core。

web //这一层是服务的消费层,用来提供前端到后台的一些交互逻辑,是个web项目。目前用来存放所有的controller,它需要依赖api和core。

core层:

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.nemo</groupId>
    <artifactId>core</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <description>核心包,用来存放一些jar和共用的东西</description>


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring.version>4.1.4.RELEASE</spring.version>
        <jackson.version>2.5.0</jackson.version>
    </properties>

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

        <!-- spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>dubbo</artifactId>
            <version>2.5.3</version>
            <exclusions>
                <exclusion>
                    <artifactId>spring</artifactId>
                    <groupId>org.springframework</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--  zookeeper -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
        </dependency>

        <dependency>
            <groupId>com.netflix.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.6</version>
        </dependency>

        <!-- servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>3.0-alpha-1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <!-- mybatis 包 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.8</version>
        </dependency>

        <!--mybatis spring 插件 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.2.2</version>
        </dependency>

        <!-- mysql连接 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.34</version>
        </dependency>

        <!-- 数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.12</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.4</version>
        </dependency>

        <!-- json -->
        <dependency>
            <groupId>org.codehaus.jackson</groupId>
            <artifactId>jackson-mapper-asl</artifactId>
            <version>1.9.13</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>
        <!-- 文件上传 -->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.4.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
    </dependencies>

</project>

目前core只是引用了这些第三方的jar。这里core就完成了。

entity层

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.nemo</groupId>
    <artifactId>entity</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <description>用来存放系统的自定义实体</description>


</project>

entity并不依赖其他的东西,是个完全独立的层。供其他层引用即可。

接下来可以用来存放一些自己定义的bean。需要注意的是,因为需要跟Dubbo结合使用,所以实体都必须实现Serializable,声明序列化,不然会有问题。

写了个简单的例子:

package com.nemo.entity;

import java.io.Serializable;

/**
 * 用户的实体
 * Created by nemo on 16-11-22.
 */
@SuppressWarnings("serial")
public class User implements Serializable {

    private int id;
    private String username;
    private String addr;

    public User(){

    }

    public User(int id,String username,String addr){
        this.id = id ;
        this.username = username;
        this.addr = addr;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }
}

到这里entity层就完成了。

api层:

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.nemo</groupId>
    <artifactId>api</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <description>接口包,用来给web和server提供通用的接口,该部分直接暴露给消费和和服务提供方</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!--使用entity中的实体-->
        <dependency>
            <groupId>com.nemo</groupId>
            <artifactId>entity</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!--说不准会有一些使用到第三方提供的实体什么的,顺便把core导进来好了-->
        <dependency>
            <groupId>com.nemo</groupId>
            <artifactId>core</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

这里写了一个简单的接口:

package com.nemo.api;

import com.nemo.entity.User;

import java.util.List;

/**用户的通用操作接口
 * Created by nemo on 16-11-22.
 */
public interface UserService {

    User getUserById(int id);
    List<User> getUserList();

    int deleteUserById(int id);

    int updateUser(User user);

    int addUser(User user);

}

至此api层也算是搞定了。接下来完成实际操作业务的server层:

server层:

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.nemo</groupId>
    <artifactId>server</artifactId>
    <version>1.0-SNAPSHOT</version>
    <description>实际业务操作层,这里的服务层会暴露给web交互层使用</description>

    <dependencies>
        <!--使用core中的实体-->
        <dependency>
            <groupId>com.nemo</groupId>
            <artifactId>core</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--使用通用接口-->
        <dependency>
            <groupId>com.nemo</groupId>
            <artifactId>api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>

这部分使用Spring +Ibaties,所以需要整合下Spring + Ibaties。

spring-ibaties.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
            http://www.springframework.org/schema/beans   
            http://www.springframework.org/schema/beans/spring-beans-4.1.xsd   
            http://www.springframework.org/schema/tx   
            http://www.springframework.org/schema/tx/spring-tx-4.1.xsd  
            http://www.springframework.org/schema/aop   
            http://www.springframework.org/schema/aop/spring-aop-4.1.xsd  
            ">

    <!-- <context:property-placeholder location="classpath:jdbc.properties"/> -->
    <!-- 配置数据源 使用的是Druid数据源 -->
    <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />

        <!-- 初始化连接大小 -->
        <property name="initialSize" value="0" />
        <!-- 连接池最大使用连接数量 -->
        <property name="maxActive" value="20" />

        <!-- 连接池最小空闲 -->
        <property name="minIdle" value="0" />
        <!-- 获取连接最大等待时间 -->
        <property name="maxWait" value="60000" />
        <property name="poolPreparedStatements" value="true" />
        <property name="maxPoolPreparedStatementPerConnectionSize"
                  value="33" />
        <!-- 用来检测有效sql -->
        <property name="validationQuery" value="${validationQuery}" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="testWhileIdle" value="true" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <!-- 打开removeAbandoned功能 -->
        <property name="removeAbandoned" value="true" />
        <!-- 1800秒,也就是30分钟 -->
        <property name="removeAbandonedTimeout" value="1800" />
        <!-- 关闭abanded连接时输出错误日志 -->
        <property name="logAbandoned" value="true" />
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat" />
    </bean>

    <!-- myBatis文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 自动扫描entity目录, 省掉Configuration.xml里的手工配置 -->
        <property name="mapperLocations" value="classpath:**/mapping/*.xml" />
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.nemo.dao" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 注解方式配置事物 -->
    <!-- <tx:annotation-driven transaction-manager="transactionManager" /> -->

    <!-- 拦截器方式配置事物 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="insert*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="delete*" propagation="REQUIRED" />

            <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="select*" propagation="SUPPORTS" read-only="true" />

        </tx:attributes>
    </tx:advice>
    

</beans>

这里的服务需要向Zookeeper注册,所以这里也需要整合下Spring + Dubbo的配置,

spring-registry.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://code.alibabatech.com/schema/dubbo
        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:zookeeper.properties</value>
                <value>classpath:jdbc.properties</value>
            </list>
        </property>
    </bean>

    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="nemo-dubbo-server" />

    <!-- 使用zookeeper广播注册中心暴露服务地址 -->
    <dubbo:registry protocol="zookeeper" address="${zookeeper.servers}" />

    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20886" />

    <!--
        官方注释:扫描注解包路径,多个包用逗号分隔,不填pacakge表示扫描当前ApplicationContext中所有的类。
        测试发现:此处package不填写包名会无法注册Service,扫描全包需填写包首即可或者填写至类的上一级目录。
     -->
    <dubbo:annotation package="com" />

</beans>

顺便贴下这边用到的一些配置:

jdbc.properties

##############JDBC CONFIG###############
#########Add by Nemo on 2016/11/22##########
#mysql version database druid setting
jdbc.initialSize=0
jdbc.maxActive=20
jdbc.minIdle=0
jdbc.maxWait=60000
jdbc.poolPreparedStatements=true
jdbc.maxPoolPreparedStatementPerConnectionSize=33
validationQuery=SELECT 1
jdbc.url=jdbc:mysql://localhost:3306/m_dubbo?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=root

zookeeper.properties

##############ZookeeperConfig###############
#########Add by Nemo on 2016/11/22##########
##提供方应用信息,用于计算依赖关系
dubbo.name=nemo-dubbo-server
##使用dubbo在本地暴露端口
dobbo.port=20886
##zookeeper中心地址,如果有多个,可以使用逗号进行分隔
zookeeper.servers=127.0.0.1:2183

log4j.properties

### set log levels ###
log4j.rootLogger = INFO , C , D , E

### console ###
log4j.appender.C = org.apache.log4j.ConsoleAppender
log4j.appender.C.Target = System.out
log4j.appender.C.layout = org.apache.log4j.PatternLayout
log4j.appender.C.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %m%n

### log file ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = ../logs/ServerManager-Info.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = INFO
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH:mm:ss}] %C.%M(%L) | %m%n

### exception ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = ../logs/Servernull
本文标签
 {{tag}}
点了个评