1. 程式人生 > >解讀Dubbo原始碼——spring-boot2整合dubbo-starter

解讀Dubbo原始碼——spring-boot2整合dubbo-starter

最近Dubbo又重新開始了維護,並且提供了和Spring-Boot整合的Dubbo-Starter。作為Dubbo的忠實使用者,真的是感動啊!這不,趕緊搭建個小Demo來爽了一把。

一、專案結構

我們一共搭建3個模組,common模組定義了服務提供者和服務消費者都需要使用的實體類和服務介面,provider模組定義了服務介面的具體實現,consumer模組定義了一個控制器並注入服務使用。

二、common模組

1、新建一個Spring-Boot專案(或模組),命名為dubbo-common。首先引入dubbo-starter的依賴,因為註冊中心使用了ZooKeeper,所以zkclient也是需要的。

        <!--dubbo-springBoot依賴-->
        <dependency>
            <groupId>com.alibaba.spring.boot</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        
        <dependency>
		    <groupId>com.101tec</groupId>
		    <artifactId>zkclient</artifactId>
		    <version>0.11</version>
        </dependency>

2、定義服務的公共介面,提供基本的增刪改查功能,並定義一個返回值的封裝類。

/**
 * 基礎介面類
 * @author z_hh  
 * @date 2018年12月28日
 */
public interface BaseSerivce<T> {
    /**
     * 新增記錄
     * @param t
     * @return
     */
    public T create(T t);
 
    /**
     * 根據主鍵刪除
     * @param pk
     * @return
     */
    public Integer delete(Long pk);
 
    /**
     * 根據主鍵更新記錄
     * @param t
     * @return
     */
    public Integer update(T t);
 
    /**
     * 根據主鍵查詢
     * @param pk
     * @return
     */
    public T get(Long pk);
}
/**
 * 結果返回類
 * @author z_hh  
 * @date 2018年12月28日
 */
public class Results {

	/**
	 * 成功
	 * @param content
	 * @return
	 */
	public static Object success(Object content) {
		Map<String, Object> map = createMap();
		map.put("success", true);
		map.put("content", content);
		return map;
	}
	
	/**
	 * 失敗
	 * @param code
	 * @param message
	 * @return
	 */
	public static Object fail(Integer code, String message) {
		Map<String, Object> map = createMap();
		map.put("success", false);
		map.put("code", code);
		map.put("message", message);
		return map;
	}
	
	private static Map<String, Object> createMap() {
		return new HashMap<>();
	}
}

3、定義學生實體類。

可以使用lombok簡化pojo的程式碼。

/**
 * 學生實體
 * @author z_hh
 * @time 2018年8月1日
 */
public class Student implements Serializable {
	
	private static final long serialVersionUID = -6913673650612146588L;

	// id
	private Long id;
	
	// 姓名
	private String name;
	
	// 性別:1男 2女
	private Integer sex;
	
	// 手機號碼
	private String mobile;
	
	// 生日
	private Date birthday;
	
	// 住址
	private String address;

	public Long getId() {
		return id;
	}

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

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getSex() {
		return sex;
	}

	public void setSex(Integer sex) {
		this.sex = sex;
	}

	public String getMobile() {
		return mobile;
	}

	public void setMobile(String mobile) {
		this.mobile = mobile;
	}

	public Date getBirthday() {
		return birthday;
	}

	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", mobile=" + mobile + ", birthday=" + birthday
				+ ", address=" + address + "]";
	}
	
}

4、定義學生服務介面類。

這裡我們就使用公共介面提供的功能,暫時不做擴充套件。

/**
 * 學生服務介面類
 * @author z_hh
 * @time 2019年1月2日
 */
public interface StudentService extends BaseSerivce<Student> {

}

5、值得注意的是,我們將一個服務註冊為Dubbo的元件時,需要在類上面使用兩個註解:Dubbo的@Service和Spring的@Component(或者@Service等)。為了方便,我們可以定義一個混合註解,同時包含Service和Component的功能。

/**
 * 包含com.alibaba.dubbo.config.annotation.Service
 * 和org.springframework.stereotype.Component
 * 的混合註解
 * @author z_hh
 * @time 2019年1月2日
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Service
@Component
public @interface DubboComponent {
	
	/* org.springframework.stereotype.Component --start */

	@AliasFor(annotation = Component.class)
	String value() default "";
	
	/* org.springframework.stereotype.Component --end */
	
	/* com.alibaba.dubbo.config.annotation.Service --start */
	
	@AliasFor(annotation = Service.class)
	Class<?> interfaceClass() default void.class;

	@AliasFor(annotation = Service.class)
    String interfaceName() default "";

	@AliasFor(annotation = Service.class)
    String version() default "";

	/* Service的其它屬性 */
    
    /* com.alibaba.dubbo.config.annotation.Service --end */
}

二、provider模組

1、新建一個Spring-Boot專案(或模組),命名為dubbo-provider。只需要引入common模組的依賴即可。

        <!-- 基礎公共模組 -->
        <dependency>
            <groupId>cn.zhh</groupId>
            <artifactId>dubbo-common</artifactId>
            <version>1.0</version>
        </dependency>

2、定義StudentService的實現類。

這裡我們使用Map模擬資料庫,並且使用自己定義的混合註解@DubboComponent宣告這是一個Dubbo的元件。

/**
 * StudentService實現類
 * @author z_hh
 * @time 2019年1月2日
 */
@DubboComponent
public class StudentServiceImpl implements StudentService {
	
	private HashMap<Long, Student> students = new HashMap<>();
	private AtomicLong idGenerator = new AtomicLong(1);
	
	// 初始化一條資料
	{
		Student initStudent = new Student();
		initStudent.setId(0L);
		initStudent.setName("zhh");
		initStudent.setSex(1);
		initStudent.setMobile("15800158000");
		initStudent.setBirthday(new Date());
		initStudent.setAddress("廣東省廣州市天河區xx街道oo巷");
		students.put(initStudent.getId(), initStudent);
	}

	@Override
	public Student create(Student t) {
		t.setId(idGenerator.getAndIncrement());
		students.put(t.getId(), t);
		return t;
	}

	@Override
	public Integer delete(Long pk) {
		if (students.containsKey(pk)) {
			students.remove(pk);
			return 1;
		}
		return 0;
	}

	@Override
	public Integer update(Student t) {
		Long id = t.getId();
		if (students.containsKey(id)) {
			students.put(id, t);
			return 1;
		}
		return 0;
	}

	@Override
	public Student get(Long pk) {
		return students.get(pk);
	}

}

3、配置。

在application.properties配置以下內容:應用名、是否開啟服務、註冊中心地址。

## Dubbo provider config
spring.dubbo.application.name=dubbo-provider
spring.dubbo.server=true
spring.dubbo.registry.address=zookeeper://localhost:2181

三、consumer模組

1、新建一個Spring-Boot專案(或模組),命名為dubbo-consumer。除了引入common模組的依賴以外,還要引入web相關的依賴。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

		<!-- 基礎公共模組 -->
        <dependency>
            <groupId>cn.zhh</groupId>
            <artifactId>dubbo-common</artifactId>
            <version>1.0</version>
        </dependency>

2、定義一個控制器,注入服務並使用。

注意,使用@Reference註解(com.alibaba.dubbo.config.annotation.Reference)引入Dubbo的元件。

/**
 * 學生控制器
 * @author z_hh
 * @time 2019年1月2日
 */
@RestController
@RequestMapping("/students")
public class StudentController {
	
	@Reference
	private StudentService service;

	@PostMapping("/")
	public Object create(@RequestBody Student student) {
		Student entity = service.create(student);
		return Results.success(entity);
	}
	
	@GetMapping("/{id:[\\d]}")
	public Object get(@PathVariable Long id) {
		Student entity = service.get(id);
		return Results.success(entity);
	}
	
	@DeleteMapping("/{id:[\\d]}")
	public Object delete(@PathVariable Long id) {
		Integer count = service.delete(id);
		if (count > 0) {
			return Results.success("刪除成功!");
		}
		return Results.fail(-1, "刪除失敗!");
	}
}

3、配置。

在application.properties配置以下內容:http訪問埠、應用名、註冊中心地址。

server.port=8082
 
## Dubbo consumer config
spring.dubbo.application.name=dubbo-consumer
spring.dubbo.registry.address=zookeeper://localhost:2181

四、註冊中心和監控中心

1、使用ZooKeeper作為註冊中心:將安裝包解壓縮,修改配置檔案,然後啟動即可(依賴Java環境,具體操作可以網上找相關資料參考)。推薦一個視覺化客戶端:ZooViewer

2、將dubbo-admin.war放到tomcat的webapps目錄下,然後啟動Tomcat即可(和ZooKeeper放在一臺伺服器,不同的話去網上查下是否需要修改什麼東西)。

3、dubbo-admin的war包和zookeeper的安裝壓縮包 已上傳到我的資源(點選下載)。

五、Demo測試

1、開啟ZooViewer,檢視ZooKeeper的目錄結構,這時候除了初始資料外什麼也沒有。

2、開啟dubbo-admin監控中心:地址ip:port/dubbo-admin,預設賬號密碼root/root。這時候服務提供者也是什麼都沒有。

3、執行dubbo-provider應用。

(1)應用啟動成功。

(2)ZooKeeper上的目錄結構發生了變化,多了根目錄dobbo、以介面名稱命名的子節點及其providers子節點。

(3) dubbo後臺監控中心也可以看到多了一個服務提供者。

4、執行dubbo-consumer應用。 

(1)應用啟動成功。

(2)ZooKeeper上的目錄結構發生了變化,介面節點下面多了consumers子節點。

(3) dubbo後臺監控中心也可以看到多了一個服務消費者。

5、訪問dubbo-consumer。

瀏覽器輸入http://localhost:8082/students/0訪問,可以看到以下結果:

至此,demo演示完畢了。

後續我們將對整個過程進行深入分析,敬請期待!!!