项目地址:https://github.com/FateSolo/Dubbo-Test
目录:
在本章将继续使用上一章的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 6 apply 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 33 subprojects { 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 59 package 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 15 package 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 13 package 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 15 package 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 35 package 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 43 package 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 35 package 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" > <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 > <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文件夹下,键入运行命令:
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日