tags: [dubbo, 分布式]
categories: [dubbo]
前言
关于分布式是什么我们为什么要弄分布式这里不赘述,这里只总结一下分布式项目从0搭建的方法和注意事项,本项目各个模块之间调用是基于Dubbo+Zookeeper来实现的
一般情况下我们都是按照服务来切分的,用以各个服务之间的解耦和切分部署,父项目为普通的Maven项目,删掉了源代码文件夹和资源文件夹,只留下光秃秃的pom.xml,来对各个服务依赖进行统筹管理
这里不使用SpringBoot作为父项项目的原因是因为SpringBoot总会继承自父项SpringBoot,子模块必然会继承相关依赖,建立空的Maven项目更加合适
代码地址
https://github.com/bwensun/Embryo/tree/master/BLOG
项目搭建
项目结构
BLOG
├─ .gitignore
├─ BLOG_API
│ ├─ BLOG_API.iml
│ ├─ pom.xml
│ ├─ src
│ │ └─ main
│ │ └─ java
├─ BLOG_USER_SERVICE
│ ├─ BLOG_USER_SERVICE.iml
│ ├─ pom.xml
│ ├─ src
│ │ ├─ main
│ │ │ ├─ java
│ │ │ └─ resources
│ │ └─ test
│ │ └─ java
├─ BLOG_WEB
│ ├─ BLOG_WEB.iml
│ ├─ pom.xml
│ ├─ src
│ │ └─ main
│ │ ├─ java
│ │ └─ resources
└─ pom.xml
项目建立
建立Maven项目父工程BLOG
正常输入GAV,groupId为项目名,artifactId为倒置的域名,版本号随意,我一般填入1.0,选择默认1.0快照版本也可以的
这里对项目的依赖做了统筹管理,在父pom中规定了依赖的版本,而子模块中只需写入GA信息即可,这样整个工程的依赖版本更迭时只需要修改父工程的依赖版本即可,另外父pom中也规定了整个项目共用了的依赖,通过依赖传递让子模块自动继承
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>BLOG_API</module>
<module>BLOG_USER_SERVICE</module>
<module>BLOG_WEB</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bwensun</groupId>
<artifactId>blog</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>blog</name>
<description>个人博客项目</description>
<properties>
<java.version>1.8</java.version>
<main.class>com.bwensun.blog.BlogApplication</main.class>
<!--maven依赖版本控制-->
<mybatis-spring-boot-starter>1.3.0</mybatis-spring-boot-starter>
<dubbo-spring-boot-starter>0.2.0</dubbo-spring-boot-starter>
<mybatis-generator-core>1.3.7</mybatis-generator-core>
<druid-spring-boot-starter>1.1.10</druid-spring-boot-starter>
<mysql-connector-java>5.1.44</mysql-connector-java>
<lombok>1.16.18</lombok>
<pagehelper>5.1.10</pagehelper>
<springfox-swagger2>2.6.1</springfox-swagger2>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.bwensun</groupId>
<artifactId>BLOG_API</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis-spring-boot-starter}</version>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo-spring-boot-starter}</version>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>${mybatis-generator-core}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid-spring-boot-starter}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java}</version>
</dependency>
</dependencies>
</dependencyManagement>
<!--公有依赖-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok}</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>${pagehelper}</version>
</dependency>
<!-- swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox-swagger2}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox-swagger2}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${main.class}</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
建立BLOG_API模块**
在Dubbo官网服务化最佳实践中有提及,建议将服务接口、服务模型、服务异常等均放在 API 包中,我们也遵循其规范,右键项目New Module -> 名为BLOG_API和父项目类似,注意设置Maven Repository设置,版本号和父项目一致,可以从pom.xml中看到继承自父项目
API模块用于定义一些公有的实体类、工具类、异常类和服务接口,调用方引入该模块,通过接口调用服务而不用关心到底是哪一个Service是实现了该功能
建立完毕后注意执行mvn clean install 安装到本地仓库
<?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">
<parent>
<artifactId>blog</artifactId>
<groupId>com.bwensun</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>BLOG_API</artifactId>
</project>
建立BLOG_USER_SERVICE模块和BLOG_Web模块
建立模块过程和之前方法一致
这里需要注意的是这两个项目我重新引入了SpringBoot相关的依赖,标记文件夹,并添加启动类,和配置文件使之成为一个单独的SpringBoot项目,用以启动和调用服务
Zookeeper的安装和启动连接:略
以下是pom.xml
和application.yaml
配置,启动类略
BLOG_USER_SERVICE模块**
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">
<parent>
<artifactId>blog</artifactId>
<groupId>com.bwensun</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>BLOG_USER_SERVICE</artifactId>
<dependencies>
<dependency>
<groupId>com.bwensun</groupId>
<artifactId>BLOG_API</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.7</version>
<configuration>
<!--自定义路径,否则会自动识别GeneratorConfig.xml文件-->
<!-- <configurationFiile>${basedir}/src/main/resources/generatorConfig.xml</configurationFile>-->
<!--是为了运行mvn mybatis-generator:generate -e时显示具体过程-->
<verbose>true</verbose>
<!--为了生成文件时后一次生成的文件覆盖前一次生成的同名文件-->
<overwrite>true</overwrite>
</configuration>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
yaml
dubbo:
application:
name: boot-order-service-consumer
registry:
#address: zookeeper://localhost:2181ip
address: zookeeper://ip:2181?backup=47.103.215.103:2182,47.103.215.103:2183
file: dubbo-registry/dubbo-registry.properties
# monitor:
# protocol: registry
server:
port: 8081
spring:
profiles:
include: druid
mybatis:
mapper-locations: classpath:mapper/*.xml
启动类
注意@EnableDubbo这个注解要加上
@SpringBootApplication
@EnableDubbo
public class BlogUserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(BlogUserServiceApplication.class, args);
}
}
BLOG_WEB模块
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">
<parent>
<artifactId>blog</artifactId>
<groupId>com.bwensun</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>BLOG_WEB</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.boot</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.bwensun</groupId>
<artifactId>BLOG_API</artifactId>
</dependency>
</dependencies>
</project>
yaml
dubbo:
application:
name: boot-order-service-consumer
registry:
address: zookeeper://IP:2181?backup=47.103.215.103:2182,47.103.215.103:2183
file: dubbo-registry/dubbo-registry.properties
# monitor:
# protocol: registry
server:
port: 8081
启动类
注意@EnableDubbo这个注解要加上
@SpringBootApplication
@EnableDubbo
public class BlogWebApplication {
public static void main(String[] args) {
SpringApplication.run(BlogWebApplication.class, args);
}
}
写个Demo
BLOG_API添加实体类、接口
- 添加实体类
@Getter
@Setter
@ToString
public class User implements Serializable {
/**
* 主键
*/
private Integer id;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String password;
private static final long serialVersionUID = 1L;
}
添加服务接口
@Component
public interface UserService {
/**
* 用户信息分页查询
* @return 分页用户对象
*/
PageInfo<User> getUserList();
}
BLOG_USER_SERVICE实现该接口
- 实现该服务接口
注意:
Service为import ``com.alibaba.dubbo.config.annotation.``Service
@Component
@Service
public class UserServiceImpl implements UserService {
@Resource
UserMapper userMapper;
@Override
public PageInfo<User> getUserList() {
PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAll();
return new PageInfo<>(users);
}
- 具体Dao层略
BLOG_WEB添加控制器
@RestController
@RequestMapping("user")
public class UserController {
@Reference
UserService userService;
@RequestMapping("userList")
public PageInfo<User> getUserList(){
return userService.getUserList();
}
}
测试
- 启动BLOG_USER_SERVICE项目来注册到ZK之中,控制台打印如下
- 启动BLOG_WEB项目,用于接受客户端的请求,同样他会连接ZK来获取服务
- 访问localhost:8081/user/userList可以发现已经接受了返回的查询结果
问题和解决
事实上,在实际测试中遇到了非常多问题,我把这些都记录下来,放到了