项目地址:
https://github.com/FateSolo/Dubbo-Test
目录:
- 0. 序
- 1. Maven构建项目
- 2. Gradle构建项目
- 3. 搭建Spring + Dubbo框架
- 4. 支持RESTful Remoting
- 5. Dubbo服务集群
- 6. ZooKeeper集群
- 7. Nginx + Tomcat集群
- 8. Redis共享session
在本章将继续使用上一章的Gradle项目,搭建一个基于Spring + Dubbo框架的分布式服务。
1. 项目导入:
使用Intellij IDEA作为项目IDE,以提高工作效率。
1) 点击Import Project,选择Dubbo-Test文件夹,点击OK。
2) 选择第二项Import Project …,选择Gradle,点击Next。
3) Gradle配置默认即可,点击Finish完成项目导入。
4) 进入Project Structure,我选择Project SDK为JDK 8,并确保Project和各个Module的Language level均跟随Project SDK。
2. 修改build.gradle
1) 将dubbo-server的build.gradle内容清空(对dubbo-api的依赖被移动到了dubbo-parent中)。
2) 将dubbo-client的build.gradle内容做如下改动(不再直接依赖dubbo-server):1
2
3
4
5
6apply plugin: 'war'
dependencies {
compile 'org.springframework:spring-webmvc:4.3.7.RELEASE'
compile 'com.google.code.gson:gson:2.7'
}
3) 将dubbo-parent的build.gradle内容做如下改动:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33subprojects {
group 'com.fatesolo'
version '1.0-SNAPSHOT'
apply plugin: 'java'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
}
}
configure(subprojects.findAll {it.name == 'dubbo-server' || it.name == 'dubbo-client'}) {
dependencies {
compile project(':dubbo-api')
compile ('com.alibaba:dubbo:2.8.4') {
exclude group: 'org.springframework'
}
compile 'org.springframework:spring-web:4.3.7.RELEASE'
compile ('com.github.sgroschupf:zkclient:0.1') {
exclude group: 'org.apache.zookeeper'
}
compile 'org.apache.zookeeper:zookeeper:3.4.10'
}
}
增加mavenLocal()是因为Maven中央库中不包含Dubbo 2.8.4版,而在Dubbo学习笔记的序章中我已经使用mvn install手动打包Dubbo 2.8.4版并安装到了本地库中。
configure内的依赖配置只针对dubbo-server和dubbo-client,两个module同时依赖dubbo-api、Dubbo框架、zkClient,并且升级了Spring框架和ZooKeeper的版本。
3. 编写dubbo-api:
1) 删去TestApi.java,增加三个子包bean、service、util。
2) 在bean包中创建Book类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59package com.fatesolo.dubbo.api.bean;
import javax.xml.bind.annotation.XmlRootElement;
import java.io.Serializable;
@XmlRootElement
public class Book implements Serializable {
private static final long serialVersionUID = 8981773984309718551L;
private int id;
private String name;
private String author;
public Book() {
}
public Book(int id, String name, String author) {
this.id = id;
this.name = name;
this.author = author;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
'}';
}
}
3) 在service包中创建BookService接口:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package com.fatesolo.dubbo.api.service;
import com.fatesolo.dubbo.api.bean.Book;
import java.util.List;
public interface BookService {
Book getBookById(int id);
List<Book> getBooks();
boolean addBook(Book book);
}
4) 在util包中创建一个StringUtil工具类:1
2
3
4
5
6
7
8
9
10
11
12
13package com.fatesolo.dubbo.api.util;
public class StringUtil {
public static boolean isBlank(String str) {
return str == null || "".equals(str.trim());
}
public static boolean isNotBlank(String str) {
return !isBlank(str);
}
}
4. 编写dubbo-server:
1) 删去TestServer.java,增加两个子包dao、service。
2) 在dao包中创建BookDao接口:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15package com.fatesolo.dubbo.server.dao;
import com.fatesolo.dubbo.api.bean.Book;
import java.util.List;
public interface BookDao {
Book findById(int id);
List<Book> findAll();
void save(Book book);
}
3) 在dao中增加子包impl,创建BookDao的一个简单实现BookDaoSimpleImpl:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35package com.fatesolo.dubbo.server.dao.impl;
import com.fatesolo.dubbo.api.bean.Book;
import com.fatesolo.dubbo.server.dao.BookDao;
import org.springframework.stereotype.Repository;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Repository
public class BookDaoSimpleImpl implements BookDao {
private final Map<Integer, Book> map = new ConcurrentHashMap<>();
@Override
public Book findById(int id) {
return map.get(id);
}
@Override
public List<Book> findAll() {
return new ArrayList<>(map.values());
}
@Override
public void save(Book book) {
int id = map.size() + 1;
book.setId(id);
map.put(id, book);
}
}
4) 在service中增加子包impl,创建BookService的实现BookServiceImpl:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43package com.fatesolo.dubbo.server.service.impl;
import com.alibaba.dubbo.container.Main;
import com.fatesolo.dubbo.api.bean.Book;
import com.fatesolo.dubbo.api.service.BookService;
import com.fatesolo.dubbo.api.util.StringUtil;
import com.fatesolo.dubbo.server.dao.BookDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class BookServiceImpl implements BookService {
@Resource(name = "bookDaoSimpleImpl")
private BookDao bookDao;
@Override
public Book getBookById(int id) {
return bookDao.findById(id);
}
@Override
public List<Book> getBooks() {
return bookDao.findAll();
}
@Override
public boolean addBook(Book book) {
if (book == null || book.getId() != 0 || StringUtil.isBlank(book.getName())) {
return false;
}
bookDao.save(book);
return book.getId() != 0;
}
public static void main(String[] args) {
Main.main(args);
}
}
其中main方法用于启动dubbo-server服务,使用了Dubbo提供的Main.main()方式。
5) 在resources/META-INF/spring目录中创建Spring配置文件dubbo-server.xml(因为Dubbo提供的Main.main()启动方式默认加载META-INF/spring/${module-name}.xml):1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<?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:context="http://www.springframework.org/schema/context"
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://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<context:component-scan base-package="com.fatesolo.dubbo.server">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<dubbo:application name="dubbo-server"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:protocol name="dubbo" port="20880"/>
<dubbo:service interface="com.fatesolo.dubbo.api.service.BookService" ref="bookServiceImpl"/>
</beans>
请注意更改ZooKeeper的地址。
5. 编写dubbo-client:
1) 删去index.jsp,增加一个子包controller。
2) 在controller包中创建BookController类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35package com.fatesolo.dubbo.client.controller;
import com.fatesolo.dubbo.api.bean.Book;
import com.fatesolo.dubbo.api.service.BookService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping(value = "/book")
public class BookController {
@Resource
private BookService bookService;
@RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = "application/xml")
public Book getBookById(@PathVariable("id") int id) {
return bookService.getBookById(id);
}
@RequestMapping(value = "", method = RequestMethod.GET, produces = "application/json")
public List<Book> getBooks() {
return bookService.getBooks();
}
@RequestMapping(value = "", method = RequestMethod.POST)
public String addBook(Book book) {
return bookService.addBook(book) ? book.toString() : "Failure";
}
}
3) 在resources/config/spring目录中创建Spring配置文件spring-common.xml:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<?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:context="http://www.springframework.org/schema/context"
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://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<context:component-scan base-package="com.fatesolo.dubbo.client">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<dubbo:application name="dubbo-client"/>
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<dubbo:reference id="bookService" interface="com.fatesolo.dubbo.api.service.BookService"/>
</beans>
ZooKeeper的地址应与dubbo-server中的一致。
4) 创建SpringMVC配置文件spring-mvc.xml:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.fatesolo.dubbo.client" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<mvc:annotation-driven/>
</beans>
5) 创建web.xml:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!-- Spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring/spring-common.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring MVC -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/spring/spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
6. 项目运行:
1) 进入ZooKeeper的bin文件夹下,键入运行命令:1
./zkServer.sh start
2) 使用IDE运行dubbo-server中BookServiceImpl的main方法,看到如下提示:1
Dubbo service server started!
3) 使用Tomcat运行dubbo-client。
4) 使用curl工具添加一条数据:1
curl -d "name=Book1&author=Fate" http://localhost:8080/dubbo-client/book
得到如下返回:1
Book{id=0, name='Book1', author='Fate'}
5) 访问http://localhost:8080/dubbo-client/book/1 ,得到如下结果:1
2
3
4
5<book>
<author>Fate</author>
<id>1</id>
<name>Book1</name>
</book>
6) 访问http://localhost:8080/dubbo-client/book ,得到如下结果:1
[{"id":1,"name":"Book1","author":"Fate"}]
7) 访问dubbo-admin管理端,可以看到服务提供者和服务消费者中已经分别显示了dubbo-server和dubbo-client。
作者 [@FateSolo]
2017 年 05月 31日