手把手教你如何玩转Dubbo(入门到项目实践)

文章目录

  • 情景引入
  • 问题引入
  • 简介
    • Dubbo官网
    • Dubbo作用
    • RPC
    • 最简单的Dubbo架构关系
  • 搭建项目的前置知识
    • 开发工具
    • 项目基本结构解析
  • 正式搭建"生产者角色"环境的工程
    • 创建父模块
    • 创建Client子模块
    • 创建Model子模块
    • 创建Service子模块
    • 父模块和子模块创建完成的结构
    • 导入相关的依赖
    • service模块创建需要的配置文件
    • 本地创建Zookeeper环境
      • 安装步骤:
    • 本地创建数据库
    • service模块创建项目的启动类
    • Client模块编写统一接口返回类
      • 统一消息返回的枚举类
      • 统一返回类:
    • Service模块编写项目环境搭建是否成功的Controller测试类
      • BaseController类
    • 启动BootMoreApplication类,启动项目
    • 通过URL进行请求,判断环境搭建是否OK
      • 情况一:URL
      • 情况二:URL
      • 情况三:URL
  • 正式搭建"消费者角色"环境的工程
  • 项目实践
    • 一:商城系统调用商品系统(RPC接口)
      • 场景描述
        • 项目实战开发
          • 生产者代码(不分先后,根据自己的习惯来即可)
          • 消费者代码
    • 二:商品系统调用订单系统(Rest接口调用)
      • 描述:在商品系统中,用户可以通过商品系统进行对应商品的下单(如:淘宝商城买东西),而实际的商品订单系统又是属于另外一个模块,而并非在商品系统中,所以这时候就出现一个系统调用另外一个系统的接口来完成功能。
      • 实践流程
        • 生产者(订单系统)
        • 消费者(商品系统)
    • 未完待续

情景引入

小白:呜呜,呜呜,呜呜,呜呜
我:谁呀,这么早就闹个不停,好好的周末还让不让人休息了
小白:呜呜,呜呜----------(声音越来越大)
我:不甘心的爬起来,想一看究竟。
小白:你,你,你,怎么就起来了呢?
我:你这么大声,猪都要被你叫醒来了。怎么了,你这是,谁欺负你了吗?
小白:是的,公司老大让我重构公司的系统项目,因为原来业务量不大,用的架构都比较简单,而且还是跑单机模式,但是现在业务量来了,前期埋的坑,现在都暴露出来了,所以,公司让我好好重构下项目。
我:那这不是挺好的吗?可以从大局思考,提升自己的能力的机会来了。
小白:可是,之前公司一个项目就是一个服务,其中存在不同项目重复的写轮子的情况,现在公司要求拆分,而且将基础常用的做成中台,服务与服务之间又要能相互调用。这可怎么办呢?
我:emmm,你的意思是说,现在需要按服务拆分项目,然后服务与服务之间能够互相调用,减少重复代码和代码的冗余以及提升整体架构性能是吗?
小白:对呀,就是,现在不知道服务与服务之间该怎么调用才好。总不能,有一个调用请求就发一个Http请求去调用吧。这效率该多慢。
我:是的,这样的情况就不该采取单一的Http了,不过我可以教你另外的一种方法哦。
小白:你知道其他的方法吗?快告诉我快告诉我。。。(立马不哭)
我:哎,起来都起来了,那就容我教你一个新知识吧。
PS:引文中出现了一个名词:中台。如果不明白的,建议百度一下,这个在公司还是很常见的。

问题引入

分布式架构是现在越来越主流的模式,并且采取的也是服务与服务之间的调用,而将许多常用的服务就可以作为中台,方便多个服务去根据需求自由获得理想的结果。其中服务可以简单的理解为一个功能模块,比如,淘宝其中最为主要的就有订单模块和商品模块。试想,如果采取单一项目架构的方式,将商品模块和订单模块都放一起,如何能撑得住双11的购物狂潮,如果一个模块接口挂了,那么后续将会损失多少金钱,所以,为了增加项目的性能,开始采取前后端分离,服务拆分,分布式部署等等的技术。
我们来分析一个问题:
假设公司存在:A,B两个服务,其中都有权限校验,另外,A还会调用B服务的接口查询某些数据库的信息(A和B的数据库表结构不一样)。那么,你此时会想到如何去处理呢?
提示:将权限校验作为一个C服务,谁需要谁去请求C服务的权限接口即完成权限校验。
我们可以发现,其中关键的在于服务与服务之间的调度,那么如何更好的实现这功能,Dubbo给我们提供了一个好的解决办法。

简介

Dubbo官网

英文官网:http://dubbo.apache.org/en-us/
中文官网:http://dubbo.apache.org/zh-cn/

Dubbo作用

Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。(不懂?没关系,跟着我一步步来,你就明白了)

RPC

RPC(Remote Procedure Call):远程过程调用
什么叫远程?
地方A有台电脑服务,地方B有台电脑服务,B调用A的服务,就叫远程。B调用自己的服务就叫本地。
更详细的解析,大家可以百度多了解了解。就好比我们常说的TCP/UDP/HTTP。

最简单的Dubbo架构关系

手把手教你如何玩转Dubbo(入门到项目实践)_第1张图片
生产者:提供服务;
消费者:消费(使用)服务;
注册中心:服务注册与发现,连接生产者与消费者,一般使用Zookeeper;
监控管理:对生产者与消费者的“健康”管理;

搭建项目的前置知识

开发工具

  • IDEA(代码编辑,Eclipse也可以,看心情即可)
  • Mysql(数据库,你喜欢Oracle也可以)
  • Navicat(mysql数据库可视化工具)
  • Postman(模拟访问请求工具)
  • JDK,Maven,Tomcat,Zookeeper这些基本的还是安装安装

项目基本结构解析

手把手教你如何玩转Dubbo(入门到项目实践)_第2张图片

正式搭建"生产者角色"环境的工程

创建父模块

  1. 打开Idea编辑器
  2. 点击创建一个新工程
    手把手教你如何玩转Dubbo(入门到项目实践)_第3张图片
  3. 选择Maven
    手把手教你如何玩转Dubbo(入门到项目实践)_第4张图片
  4. 填写项目工程基本信息
    手把手教你如何玩转Dubbo(入门到项目实践)_第5张图片
    手把手教你如何玩转Dubbo(入门到项目实践)_第6张图片
  5. 大功告成
    手把手教你如何玩转Dubbo(入门到项目实践)_第7张图片

创建Client子模块

  1. 选中父模块中的项目名,然后右击,选择new和model
    手把手教你如何玩转Dubbo(入门到项目实践)_第8张图片
  2. 同理,选择Maven
  3. 然后填写信息
    手把手教你如何玩转Dubbo(入门到项目实践)_第9张图片
  4. 创建完成
    手把手教你如何玩转Dubbo(入门到项目实践)_第10张图片

创建Model子模块

同创建Client子模块

创建Service子模块

同创建Client子模块

父模块和子模块创建完成的结构

手把手教你如何玩转Dubbo(入门到项目实践)_第11张图片

导入相关的依赖

  1. client模块导入内容


    
        dubbofirst
        com.scw.dubbo.first
        1.0.1
    
    4.0.0

    client
    
        4.1.2
        1.16.10
    

    
        
        
            javax.validation
            validation-api
            1.1.0.Final
        
        
            org.hibernate
            hibernate-validator
            5.3.5.Final
        

        
        
            com.github.pagehelper
            pagehelper
            ${mybatis-pagehelper.version}
        

        
        
            org.projectlombok
            lombok
            ${lombok.version}
        

    


  1. model模块导入内容


    
        dubbofirst
        com.scw.dubbo.first
        1.0.1
    
    4.0.0

    model
    
        1.1.1
        4.1.2
    

    
        
        
            com.scw.dubbo.first
            client
            ${project.parent.version}
        

        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            ${mybatis-spring-boot.version}
        

    


  1. service模块导入内容


    
        dubbofirst
        com.scw.dubbo.first
        1.0.1
    
    4.0.0

    service
    jar

    
        com.debug.mooc.dubbo.one.server.BootMoreApplication

        
        2.0.5.RELEASE

        1.2.0.RELEASE
        1.7.13
        1.2.17
        5.1.37
        1.0.16
        19.0
        2.9.2
        3.1

        3.4.10
        2.12.0
        2.6.4

        3.0.7.Final
        3.1.2
        2.6.1
        4.3.6

    

    
    
        
            
                org.springframework.boot
                spring-boot-dependencies
                ${spring-boot.version}
                pom
                import
            
        
    

    

        
        
            com.scw.dubbo.first
            model
            ${project.parent.version}
        

        
        
            org.slf4j
            slf4j-api
            ${slf4j.version}
        

        
            org.slf4j
            slf4j-log4j12
            ${slf4j.version}
        

        
            log4j
            log4j
            ${log4j.version}
        

        
        
            com.google.guava
            guava
            ${guava.version}
        

        
        
            joda-time
            joda-time
            ${joda-time.version}
        

        
        
            mysql
            mysql-connector-java
            ${mysql.version}
        

        
        
            com.alibaba
            druid
            ${druid.version}
        

        
        
            org.springframework.boot
            spring-boot-starter-web
            ${spring-boot.version}
            
                
                    ch.qos.logback
                    logback-classic
                
                
                    org.slf4j
                    log4j-over-slf4j
                
            
        

        
        
            org.springframework.boot
            spring-boot-starter-tomcat
            
        

        
            org.apache.tomcat.embed
            tomcat-embed-jasper
            
        

        
            javax.servlet
            jstl
        

        
        
            com.alibaba
            dubbo
            ${dubbo.version}
            
                
                    curator-framework
                    org.apache.curator
                
                
                    curator-client
                    org.apache.curator
                
                
                    guava
                    com.google.guava
                
                
                    spring-web
                    org.springframework
                
            
        

        
            cglib
            cglib
            ${cglib.version}
            
                
                    asm
                    org.ow2.asm
                
            
        

        
        
            org.apache.zookeeper
            zookeeper
            ${zookeeper.version}
            
                
                    slf4j-log4j12
                    org.slf4j
                
            
        
        
            org.apache.curator
            curator-framework
            ${curator.version}
        

        
        
            org.jboss.resteasy
            resteasy-jaxrs
            ${resteasy.version}
            
                
                    httpclient
                    org.apache.httpcomponents
                
            
        
        
            org.jboss.resteasy
            resteasy-netty
            ${resteasy.version}
        
        
            org.jboss.resteasy
            resteasy-client
            ${resteasy.version}
        
        
            org.jboss.resteasy
            resteasy-jackson-provider
            ${resteasy.version}
        


        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        

        
        
            com.google.code.gson
            gson
            ${gson.version}
        

        
        
            com.squareup.okhttp3
            okhttp
            ${okhttp.version}
        

    

    
        dubboOne-${project.parent.version}
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                ${spring-boot.version}
                
                    
                        
                            repackage
                        
                    
                
            
            
                org.apache.maven.plugins
                maven-war-plugin
                2.4
                
                    false
                
            
        

        
            
                src/main/resources
                true
            
        
    


  1. 父模块导入内容


    4.0.0

    com.scw.dubbo.first
    dubbofirst
    pom
    1.0.1
    
        client
        model
        service
    
    
        UTF-8
        1.8
        ${java.version}
        ${java.version}
    


service模块创建需要的配置文件

结构如下:
手把手教你如何玩转Dubbo(入门到项目实践)_第12张图片

  • applicaion.properties
# dubbo提供服务的端口
server.port=8093
#server.context-path=/dubbofirst
# dubbo上下文路径
server.servlet.context-path=/dubbofirst
#logging
logging.path=E:\\logs\\mooc\\dubboOne\\logs
logging.file=dubboOne
logging.level.org.springframework = INFO
logging.level.com.fasterxml.jackson = INFO
logging.level.com.debug.mooc.dubbo.one = debug
#json时间解析配置
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
#spring.datasource.initialize=false
spring.datasource.initialization-mode=never
spring.jmx.enabled=false
#数据库配置
datasource.url=jdbc:mysql://127.0.0.1:3306/dubbofirst?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
datasource.username=root
datasource.password=xxxxxxx
#mybatis
mybatis.config-location=classpath:mybatis-config.xml
mybatis.checkConfigLocation = true
mybatis.mapper-locations=classpath:mappers/*.xml
#dubbo zookeeper本地配置
dubbo.registry.address=zookeeper://127.0.0.1:2181
  • dubbo.properties
dubbo.container=log4j,spring
dubbo.reference.check=false
dubbo.registry.client=curator
dubbo.application.name=dubboFirst-provider
dubbo.application.owner=debug
#dubbo提供rpc服务接口
dubbo.protocol.name=dubbo
dubbo.protocol.dubbo.port=20903
#dubbo提供rest服务接口
dubbo.protocol.name=rest
dubbo.protocol.rest.port=9013
dubbo.protocol.rest.server=tomcat
dubbo.service.loadbalance=roundrobin
  • log4j.propertis
#Console Log
log4j.rootLogger=INFO,console,debug,info,warn,error

LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] boot%X{context} - %5p [%t] --- %c{1}: %m%n

#A1--Print log to Console
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=${LOG_PATTERN}

log4j.appender.info=org.apache.log4j.DailyRollingFileAppender
log4j.appender.info.Threshold=INFO
log4j.appender.info.File=${LOG_PATH}/${LOG_FILE}_info.log
log4j.appender.info.DatePattern='.'yyyy-MM-dd
log4j.appender.info.layout = org.apache.log4j.PatternLayout
log4j.appender.info.layout.ConversionPattern=${LOG_PATTERN}

log4j.appender.error=org.apache.log4j.DailyRollingFileAppender
log4j.appender.error.Threshold=ERROR
log4j.appender.error.File=${LOG_PATH}/${LOG_FILE}_error.log
log4j.appender.error.DatePattern='.'yyyy-MM-dd
log4j.appender.error.layout = org.apache.log4j.PatternLayout
log4j.appender.error.layout.ConversionPattern=${LOG_PATTERN}


log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender
log4j.appender.debug.Threshold=DEBUG
log4j.appender.debug.File=${LOG_PATH}/${LOG_FILE}_debug.log
log4j.appender.debug.DatePattern='.'yyyy-MM-dd
log4j.appender.debug.layout = org.apache.log4j.PatternLayout
log4j.appender.debug.layout.ConversionPattern=${LOG_PATTERN}

log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender
log4j.appender.warn.Threshold=WARN
log4j.appender.warn.File=${LOG_PATH}/${LOG_FILE}_warn.log
log4j.appender.warn.DatePattern='.'yyyy-MM-dd
log4j.appender.warn.layout = org.apache.log4j.PatternLayout
log4j.appender.warn.layout.ConversionPattern=${LOG_PATTERN}
  • mybatis-config.xml



    
        
        
        
        
        
        
        
        
        
        
    

    

    
        
    
    
        
        
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
            
        
    

  • spring-jdbc.xml



    
    
        
        
        
        

        
        
        
        

        
        

        
        

        
        

        
        
        
        

        
        
        

        
        
    

    
        
    


  • spring-dubbo.xml



    
    

    
    

    
    
    

    
	




本地创建Zookeeper环境

下载链接:https://zookeeper.apache.org/releases.html
或者
https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/

安装步骤:

  1. 解压文件
  2. 在conf目录复制一份zoo_sample.cfg文件,并命名为zoo.cfg
  3. 在zoo.cfg修改data的目录,并且创建log存放的目录,如图所示:其中的data和log目录需要自己创建的哦!!!!
    在这里插入图片描述
  4. 运行bin目录下面的zkServicer,cmd,查看是否运行成功即可。(zookeeper默认的端口是:2181)

本地创建数据库

创建一个数据库,其名字要与配置文件中的配的一样(比如我配置的是:dubbofirst)

service模块创建项目的启动类

BootMoreApplication:

package com.scw.dubbo.first.service;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@ImportResource(value = {"classpath:spring/spring-jdbc.xml", "classpath:spring/spring-dubbo.xml"})
@MapperScan(basePackages = "com.scw.dubbo.first.model.mapper")
public class BootMoreApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(BootMoreApplication.class);
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(BootMoreApplication.class, args);
    }

}

Client模块编写统一接口返回类

统一消息返回的枚举类

package com.scw.dubbo.first.client.enums;

/**
 * 统一接口的返回信息枚举类
 */
public enum  StatusCode {

    Success(0,"成功"),
    Fail(-1,"失败"),
    InvalidParams(200,"无效的参数"),
    ItemNotExist(201,"商品不存在!");

    private Integer code;
    private String msg;

    StatusCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

统一返回类:

package com.scw.dubbo.first.client.response;
import com.scw.dubbo.first.client.enums.StatusCode;
import java.io.Serializable;

/**
 * 统一返回类(便于统一返回信息)
 * @param 
 */
public class BaseResponse implements Serializable{

    private Integer code;
    private String msg;
    private T data;

    public BaseResponse() {
    }

    public BaseResponse(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public BaseResponse(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public BaseResponse(StatusCode statusCode) {
        this.code = statusCode.getCode();
        this.msg = statusCode.getMsg();
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

Service模块编写项目环境搭建是否成功的Controller测试类

BaseController类

package com.scw.dubbo.first.service.controller;
import com.scw.dubbo.first.client.enums.StatusCode;
import com.scw.dubbo.first.client.response.BaseResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class BaseController {

    private static final Logger log= LoggerFactory.getLogger(BaseController.class);

    private static final String prefix="base";

    /**
     * 测试环境搭建是否成功
     * @param param
     * @return
     */
    @RequestMapping(value = prefix+"/test",method = RequestMethod.GET)
    public BaseResponse one(@RequestParam String param){
        BaseResponse response=new BaseResponse(StatusCode.Success);
        try {
            response.setData(param);

        }catch (Exception e){
            e.printStackTrace();
            response=new BaseResponse(StatusCode.Fail);
        }
        return response;
    }
}

启动BootMoreApplication类,启动项目

如果启动项目发生错误,那么就根据错误进行提示修改哦。。。如果认真的按照我的步骤来的话,肯定是没问题的哈。

通过URL进行请求,判断环境搭建是否OK

情况一:URL

手把手教你如何玩转Dubbo(入门到项目实践)_第13张图片
原因:因为我们在application.properties配置文件中,将端口改为了8093,所以现在还在用原来的8080当然是不可以的啦。!!!!

情况二:URL

手把手教你如何玩转Dubbo(入门到项目实践)_第14张图片
What,怎么还不行,说好的是8093的呢?这是因为我们在application.properties文件中配置了dubbo的上下文是dubbofirst,所以这个链接当然不行了,继续往下面看~~~

情况三:URL

手把手教你如何玩转Dubbo(入门到项目实践)_第15张图片

哇塞,,,,终于可以了,历经千辛万苦~~~~感动

备注:请大家一定要认真的研读配置文件的内容,一定要知道是干嘛的,是在哪里进行使用的,因为后续会非常多的使用到这些内容。基础扎实才决定我们能走多远~!

正式搭建"消费者角色"环境的工程

这个搭建与“生产者角色”的搭建过程一样,只是在配置文件中需要修改部分内容。

  1. 数据库的连接信息(与生产者的不一样即可)
  2. dubbo的相关端口(与生产者的不一样即可,因为现在搭建的都是在本地进行配置和部署,所以防止端口冲突)
    因此,这个搭建就不多说了。请返回上面进行重新搭建。

项目实践

一:商城系统调用商品系统(RPC接口)

场景描述

该场景分为两个角色:生产者(商品系统)和消费者(商城系统),而商城系统就通过调用商品系统而查询到对应的商品信息而进行显示。(备注:简单演示消费者调用生产者的服务)

项目实战开发

生产者代码(不分先后,根据自己的习惯来即可)
  1. 在client模块编写生产者服务的接口,命名IDubboItemService
package com.scw.dubbo.first.client.service;

import com.scw.dubbo.first.client.response.BaseResponse;

public interface IDubboItemService {

    /**
     * 查询商品
     * @return
     */
    BaseResponse listItems();
}
  1. 在service模块编写代码,命名为DubboItemServiceImpl
package com.scw.dubbo.first.service.dubbo;
import com.alibaba.dubbo.config.annotation.Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.scw.dubbo.first.client.enums.StatusCode;
import com.scw.dubbo.first.client.response.BaseResponse;
import com.scw.dubbo.first.client.service.IDubboItemService;
import com.scw.dubbo.first.model.entity.ItemInfo;
import com.scw.dubbo.first.model.mapper.ItemInfoMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import java.util.List;

/**
 * 通过注解将其进行发布到注册中心
 */
@Service(protocol = {"dubbo","rest"},validation = "true",version = "1.0",timeout = 3000)
@Path("goods/")
public class DubboItemServiceImpl implements IDubboItemService {

    private static final Logger log= LoggerFactory.getLogger(DubboItemService.class);

    @Autowired
    private ItemInfoMapper itemInfoMapper;

    /**
     * 列表查询服务-实际的业务实现逻辑
     * @return
     */
    @Path("item/list")
    public BaseResponse listItems() {
        BaseResponse response=new BaseResponse(StatusCode.Success);
        try {
            List infos=itemInfoMapper.selectAll();
            log.info("查询到的商品列表数据:{} ",infos);
            response.setData(infos);

        }catch (Exception e){
            log.error("列表查询服务-实际的业务实现逻辑-发生异常:",e.fillInStackTrace());
            response=new BaseResponse(StatusCode.Fail);
        }
        return response;
    }
}
  1. 编写数据处理层Mapper映射类
package com.scw.dubbo.first.model.mapper;
import com.scw.dubbo.first.model.entity.ItemInfo;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface ItemInfoMapper {

    List selectAll();
}
  1. 编写对应Mapper类的xml文件

 


  
    
    
    
    
    
    
    
  
  
    id, code, name, price, is_active, create_time, update_time
  
  
  
  1. ItemInfo商品对应的实体类
package com.scw.dubbo.first.model.entity;

import lombok.ToString;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

@ToString
public class ItemInfo implements Serializable{
    private Integer id;

    private String code;

    private String name;

    private BigDecimal price;

    private Integer isActive;

    private Date createTime;

    private Date updateTime;

    public Integer getId() {
        return id;
    }

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

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code == null ? null : code.trim();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getIsActive() {
        return isActive;
    }

    public void setIsActive(Integer isActive) {
        this.isActive = isActive;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}
  1. 将对应的生产者代码通过maven打成jar包生成依赖,便于消费者使用
    手把手教你如何玩转Dubbo(入门到项目实践)_第16张图片
  2. 初始化数据库的数据
/*
Navicat MySQL Data Transfer

Source Server         : mylocal
Source Server Version : 50717
Source Host           : localhost:3306
Source Database       : dubbofirst

Target Server Type    : MYSQL
Target Server Version : 50717
File Encoding         : 65001

Date: 2019-10-07 18:38:48
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `item_info`
-- ----------------------------
DROP TABLE IF EXISTS `item_info`;
CREATE TABLE `item_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `code` varchar(255) DEFAULT NULL COMMENT '商品编码',
  `name` varchar(255) DEFAULT NULL COMMENT '名称',
  `price` decimal(15,2) DEFAULT NULL COMMENT '销售价',
  `is_active` int(11) DEFAULT '1' COMMENT '是否有效(1=是;0=否)',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_code` (`code`) USING BTREE COMMENT '商品编码唯一'
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8 COMMENT='商品信息表';

-- ----------------------------
-- Records of item_info
-- ----------------------------
INSERT INTO `item_info` VALUES ('1', '10010', 'Java编程思想', '58.50', '1', '2019-01-13 14:07:58', null);
INSERT INTO `item_info` VALUES ('2', '10011', '由浅入深实战分布式中间件', '45.50', '1', '2019-01-13 14:07:59', null);
INSERT INTO `item_info` VALUES ('3', '10012', 'Dubbo实战教程', '68.00', '1', '2019-01-13 14:07:59', null);
INSERT INTO `item_info` VALUES ('4', '10013', 'JVM深入实战指南', '38.50', '1', '2019-01-13 14:07:59', null);
INSERT INTO `item_info` VALUES ('5', '10014', 'SpringBoot实战', '56.50', '1', '2019-01-13 14:07:59', null);
INSERT INTO `item_info` VALUES ('6', '10015', 'SpringCloud实战', '64.00', '1', '2019-01-13 14:07:59', null);
INSERT INTO `item_info` VALUES ('7', '10017', 'Mysql实战指南', '75.50', '1', '2019-01-13 14:07:59', null);

消费者代码
  1. service模块的pom.xml文件添加对生产者的依赖配置

            com.scw.dubbo.first
            client
            1.0.1
        
  1. 在service模块中的spring-dubbo.xml文件添加生产者服务的配置
  
    
    

备注:其中的url如果是本地的话可以不写,因为在自测的时候都是在本的,而如果以后在实际项目后,我们可以设置生产者服务对应的服务器的IP地址和端口;

  1. 消费者编写调用生产者接口的业务代码
package com.scw.dubbo.second.service.controller;
import com.google.common.collect.Maps;
import com.scw.dubbo.first.client.response.BaseResponse;
import com.scw.dubbo.first.client.service.IDubboItemService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;

@RestController
public class ItemController {

    private static final Logger log= LoggerFactory.getLogger(ItemController.class);

    private static final String prefix="item";
	//之所以能够引入生产者中的类,就是因为我们在pom.xml文件引入了生产者的依赖
    @Autowired
    private IDubboItemService dubboItemService;



    /**
     * 用户商城列表查询
     * @return
     */
    @RequestMapping(value = prefix+"/list",method = RequestMethod.GET)
    public Map list(){
        Map resMap= Maps.newHashMap();
        resMap.put("code","0");
        resMap.put("msg","成功");

        //TODO:调用服务提供方dubboFirst提供的列表查询功能
        try {
            BaseResponse response = dubboItemService.listItems();
            if (response!=null && response.getCode().equals(0)){
                resMap.put("data",response.getData());
            }
        }catch (Exception e){
            e.printStackTrace();
            resMap.put("code","-1");
            resMap.put("msg","失败");
        }
        return resMap;
    }

  1. 启动服务,查看结果
    当消费者要使用生产者的接口服务的时候,一定要保证生产者的服务是处于可用状态,否则会提示无法调用连接生产者的接口。
    因此,别忘记启动笨的的zookeeper服务哦,然后再启动生产者服务,最后再启动消费者服务即可;
  2. 通过URL请求,查看调用结果
    手把手教你如何玩转Dubbo(入门到项目实践)_第17张图片
    备注:我们可以看到通过消费者确实调用到了生产者的接口,并且也达到了我们预想的结果。

二:商品系统调用订单系统(Rest接口调用)

描述:在商品系统中,用户可以通过商品系统进行对应商品的下单(如:淘宝商城买东西),而实际的商品订单系统又是属于另外一个模块,而并非在商品系统中,所以这时候就出现一个系统调用另外一个系统的接口来完成功能。

PS:其实这个接口可以像上面的例子一样采取RPC的调用方式,但是为了让大家了解不同的调用方式,所以这个实例就采取Rest的调用方式。大家有兴趣的话,也可以将下面这个例子改成用RPC的形式哟。!

实践流程

**备注:**下面的生产者和消费者,我就默认和上面的实例保持一样了,所以基本的环境搭建就不重复说,就只是说如何完成这个功能,如果环境还没搭好的,那么就认真看看上面的例子哟。

生产者(订单系统)

  1. 数据库设计
    MysqlDDL语句:
CREATE TABLE `order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `item_id` int(11) NOT NULL,
  `customer_name` varchar(255) NOT NULL,
  `total` int(11) NOT NULL,
  `order_time` datetime NOT NULL,
  `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

数据库内容:
手把手教你如何玩转Dubbo(入门到项目实践)_第18张图片
备注:大家主要是理解这个过程,而不是在于看数据库的结构设计,仅仅是一个示例;

  1. 订单order实体类
package com.scw.dubbo.first.client.model;

import lombok.Data;
import lombok.ToString;

import java.io.Serializable;
import java.util.Date;

/**
 * @ Author     :scw
 * @ Date       :Created in 下午 4:55 2019/11/24 0024
 * @ Description:订单实体
 * @ Modified By:
 * @Version: $version$
 */
@Data
@ToString
public class Order implements Serializable{
    //主键
    private Integer id;
    //商品ID
    private Integer itemId;
    //用户名
    private String customerName;
    //商品总数量
    private Integer total;
    //下单时间
    private Date orderTime;
    //更新时间
    private Date updateTime;

    public Integer getId() {
        return id;
    }

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

    public Integer getItemId() {
        return itemId;
    }

    public void setItemId(Integer itemId) {
        this.itemId = itemId;
    }

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    public Integer getTotal() {
        return total;
    }

    public void setTotal(Integer total) {
        this.total = total;
    }

    public Date getOrderTime() {
        return orderTime;
    }

    public void setOrderTime(Date orderTime) {
        this.orderTime = orderTime;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}

  1. Rest接口
package com.scw.dubbo.first.client.service;

import com.scw.dubbo.first.client.model.Order;
import com.scw.dubbo.first.client.response.BaseResponse;

/**
 * @ Author     :scw
 * @ Date       :Created in 下午 4:53 2019/11/24 0024
 * @ Description:订单Rest接口
 * @ Modified By:
 * @Version: $version$
 */
public interface OrderService {

    /**
     * 客户商品下单
     * @param order
     * @return
     */
    BaseResponse addCustomerProductOrder(Order order);

}

  1. Rest接口实现类(真正提供服务的接口)
package com.scw.dubbo.first.service.rest;

import com.alibaba.dubbo.config.annotation.Service;
import com.scw.dubbo.first.client.enums.StatusCode;
import com.scw.dubbo.first.client.model.Order;
import com.scw.dubbo.first.client.response.BaseResponse;
import com.scw.dubbo.first.client.service.OrderService;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.Date;

/**
 * @ Author     :scw
 * @ Date       :Created in 下午 4:59 2019/11/24 0024
 * @ Description:客户商品下单rest接口实现类
 * @ Modified By:
 * @Version: $version$
 */
@Path("order")
@Service(protocol = {"dubbo","rest"},validation = "true",version = "1.0",timeout = 3000)
public class OrderServiceImpl implements OrderService {

    /**
     * 客户商品下单的POST请求的接口
     * 备注:consumes和produces注解就是将其接口设置的接受类型(还有很多比如XML,都可以)
     * @param order
     * @return
     */
    @Path("/add")
    @POST
    @Consumes(value = MediaType.APPLICATION_JSON)
    @Produces(value = MediaType.APPLICATION_JSON)
    @Override
    public BaseResponse addCustomerProductOrder(Order order) {
        BaseResponse baseResponse = new BaseResponse(StatusCode.Success);
        //TODO 随便写一些处理逻辑,这个一般还有数据层的操作,这里就不演示了
        if(order == null){
            baseResponse.setCode(StatusCode.Fail.getCode());
            baseResponse.setMsg("订单数据不能为空");
            return baseResponse;
        }
        //TODO 一些逻辑处理,这里就不多写了
        order.setUpdateTime(new Date());
        return baseResponse;
    }

}

  1. 启动服务,自测是否接口成功
    备注:
    大家可以下一个Postman的软件用于发出请求,或者采取IDEA自带的插件也是可以的。
    (1)情景如下
    手把手教你如何玩转Dubbo(入门到项目实践)_第19张图片
    原因解析: 很多人都不知道到底请求的时候是采取服务的上下文前缀(如:dubbofirst)还是dubbo暴露出去的上下前缀(如:v1,这个是在上面的例子配的,大家根据自己需求配置即可,我这是为了方便),大家可以理解为:如果当前的接口是dubbo或者rest接口那么我们就要采取dubbo的上下文;如果是用的controller层的请求,那么自然就是用的项目服务的上下文;
    application.properties配置文件:
    手把手教你如何玩转Dubbo(入门到项目实践)_第20张图片
    spring.dubbo.xml配置文件:
    在这里插入图片描述
    (2)情景如下:
    手把手教你如何玩转Dubbo(入门到项目实践)_第21张图片
    备注: 很明显,咱们写的rest接口没有问题,那么如何给另外一个服务进行调用呢?那么继续往下看,let‘s go

消费者(商品系统)

  1. Controller类(功能:用于本服务对外提供请求接口)
package com.scw.dubbo.second.service.controller;

import com.google.common.collect.Maps;
import com.scw.dubbo.second.client.model.OrderDto;
import com.scw.dubbo.second.service.rest.OrderServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.ws.rs.core.MediaType;
import java.util.Map;

/**
 * @ Author     :scw
 * @ Date       :Created in 下午 5:37 2019/11/24 0024
 * @ Description:商城系统的客户下单请求controller
 *
 * @ Modified By:
 * @Version: $version$
 */
@RestController
public class OrderController {

    private static final Logger log= LoggerFactory.getLogger(OrderController.class);

    @Autowired
    private OrderServiceImpl orderService;

    /**
     * 客户下订单接口
     * 备注:consumes就是限制了前端是传Json格式的参数,如果不加,那么就以post请求的方式传Key 和 value即可(原始的方式)
     * @param order
     * @return
     */
    @RequestMapping(value = "/add", method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON)
    public Map addCustomerOrder(@RequestBody OrderDto order){
        Map resMap= Maps.newHashMap();
        resMap.put("code","0");
        resMap.put("msg","成功");
        try {
            //将调用另外一个系统的接口写在service层便于逻辑处理
            orderService.addOrder(order);
        }catch (Exception e){
            log.error("客户下单发生异常:" + e);
        }

        return resMap ;
    }

}

  1. 采用Rest调用生产者的下单服务接口OrderServiceImpl
    备注:其中示例采用的post请求是使用OKhttp对象,大家也可以采取其他的对象都是可以的哦!!
package com.scw.dubbo.second.service.rest;

import com.scw.dubbo.second.client.model.OrderDto;
import com.scw.dubbo.second.service.controller.ItemController;
import okhttp3.*;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

/**
 * @ Author     :scw
 * @ Date       :Created in 下午 5:50 2019/11/24 0024
 * @ Description:调用其他服务rest接口的实现类
 * @ Modified By:
 * @Version: $version$
 */
@Service
public class OrderServiceImpl {

    //调用其他服务rest接口的请求URL
    private static final String ORDER_URL = "http:127.0.0.1:9013/v1/order/add";

    //将对象序列化的对象
    private ObjectMapper objectMapper = new ObjectMapper();

    //采取okhttp的对象来实现post请求
    private OkHttpClient okHttpClient = new OkHttpClient();

    private static final Logger log= LoggerFactory.getLogger(OrderServiceImpl.class);


    /**
     * 处理用户下单的逻辑
     * @param orderDto
     */
    public void addOrder(OrderDto orderDto) {
        try {
            //(1)构造builder对象
            Request.Builder builder = new Request.Builder().url(ORDER_URL).header("Content-Type", "application/json");
            //(2)构造消息体
            RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), objectMapper.writeValueAsString(orderDto));
            //(3)构造请求
            Request request = builder.post(requestBody).build();
            //(4)发送请求
            Response response = okHttpClient.newCall(request).execute();
            //(5)打印响应内容
            log.info("响应结果为:" + response.body().toString());
        } catch (Exception e) {

        }
    }

}

  1. 启动服务,测试结果
    (1)场景如下:
    手把手教你如何玩转Dubbo(入门到项目实践)_第22张图片
    原因分析: 这是因为当前的服务是调用方,并且调用的是controller层的请求,那么URL对应的上下文应该是系统服务,而不是dubbo的上下文。因此,应该将v2改为dubboSecond即可(具体可以对比下面的一种场景)。
    (2)场景如下:
    手把手教你如何玩转Dubbo(入门到项目实践)_第23张图片
    是否会怀疑到底是不是另外一个服务提供的服务呢?那我们回到IDEA则可以看到提供服务系统成功的打印了如下的信息,所以成功调用另外服务的rest接口啦!
    手把手教你如何玩转Dubbo(入门到项目实践)_第24张图片
    哇塞。。。。。调用成功,基本流程已经打通,是不是觉得其实就和调用自己系统的方法一样呢?so easy!!!

  2. 调用post方法的优化
    备注:上面调用的方式其实很简单,但是不够通用,为了系统能够更好的调用Post和Get类型的请求,我们应该封装一下,达到更好的优化效果!
    (后续再补啦,一下接受太多不太好,大家赶紧先去试试感觉!!!!!!!!!!)

未完待续

你可能感兴趣的:(JavaWeb)