小白:呜呜,呜呜,呜呜,呜呜
我:谁呀,这么早就闹个不停,好好的周末还让不让人休息了
小白:呜呜,呜呜----------(声音越来越大)
我:不甘心的爬起来,想一看究竟。
小白:你,你,你,怎么就起来了呢?
我:你这么大声,猪都要被你叫醒来了。怎么了,你这是,谁欺负你了吗?
小白:是的,公司老大让我重构公司的系统项目,因为原来业务量不大,用的架构都比较简单,而且还是跑单机模式,但是现在业务量来了,前期埋的坑,现在都暴露出来了,所以,公司让我好好重构下项目。
我:那这不是挺好的吗?可以从大局思考,提升自己的能力的机会来了。
小白:可是,之前公司一个项目就是一个服务,其中存在不同项目重复的写轮子的情况,现在公司要求拆分,而且将基础常用的做成中台,服务与服务之间又要能相互调用。这可怎么办呢?
我:emmm,你的意思是说,现在需要按服务拆分项目,然后服务与服务之间能够互相调用,减少重复代码和代码的冗余以及提升整体架构性能是吗?
小白:对呀,就是,现在不知道服务与服务之间该怎么调用才好。总不能,有一个调用请求就发一个Http请求去调用吧。这效率该多慢。
我:是的,这样的情况就不该采取单一的Http了,不过我可以教你另外的一种方法哦。
小白:你知道其他的方法吗?快告诉我快告诉我。。。(立马不哭)
我:哎,起来都起来了,那就容我教你一个新知识吧。
PS:引文中出现了一个名词:中台。如果不明白的,建议百度一下,这个在公司还是很常见的。
分布式架构是现在越来越主流的模式,并且采取的也是服务与服务之间的调用,而将许多常用的服务就可以作为中台,方便多个服务去根据需求自由获得理想的结果。其中服务可以简单的理解为一个功能模块,比如,淘宝其中最为主要的就有订单模块和商品模块。试想,如果采取单一项目架构的方式,将商品模块和订单模块都放一起,如何能撑得住双11的购物狂潮,如果一个模块接口挂了,那么后续将会损失多少金钱,所以,为了增加项目的性能,开始采取前后端分离,服务拆分,分布式部署等等的技术。
我们来分析一个问题:
假设公司存在:A,B两个服务,其中都有权限校验,另外,A还会调用B服务的接口查询某些数据库的信息(A和B的数据库表结构不一样)。那么,你此时会想到如何去处理呢?
提示:将权限校验作为一个C服务,谁需要谁去请求C服务的权限接口即完成权限校验。
我们可以发现,其中关键的在于服务与服务之间的调度,那么如何更好的实现这功能,Dubbo给我们提供了一个好的解决办法。
英文官网:http://dubbo.apache.org/en-us/
中文官网:http://dubbo.apache.org/zh-cn/
Dubbo是一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。(不懂?没关系,跟着我一步步来,你就明白了)
RPC(Remote Procedure Call):远程过程调用
什么叫远程?
地方A有台电脑服务,地方B有台电脑服务,B调用A的服务,就叫远程。B调用自己的服务就叫本地。
更详细的解析,大家可以百度多了解了解。就好比我们常说的TCP/UDP/HTTP。
生产者:提供服务;
消费者:消费(使用)服务;
注册中心:服务注册与发现,连接生产者与消费者,一般使用Zookeeper;
监控管理:对生产者与消费者的“健康”管理;
同创建Client子模块
同创建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}
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}
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
4.0.0
com.scw.dubbo.first
dubbofirst
pom
1.0.1
client
model
service
UTF-8
1.8
${java.version}
${java.version}
# 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.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
#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}
下载链接:https://zookeeper.apache.org/releases.html
或者
https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/
创建一个数据库,其名字要与配置文件中的配的一样(比如我配置的是:dubbofirst)
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);
}
}
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;
}
}
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;
}
}
如果启动项目发生错误,那么就根据错误进行提示修改哦。。。如果认真的按照我的步骤来的话,肯定是没问题的哈。
原因:因为我们在application.properties配置文件中,将端口改为了8093,所以现在还在用原来的8080当然是不可以的啦。!!!!
What,怎么还不行,说好的是8093的呢?这是因为我们在application.properties文件中配置了dubbo的上下文是dubbofirst,所以这个链接当然不行了,继续往下面看~~~
哇塞,,,,终于可以了,历经千辛万苦~~~~感动
备注:请大家一定要认真的研读配置文件的内容,一定要知道是干嘛的,是在哪里进行使用的,因为后续会非常多的使用到这些内容。基础扎实才决定我们能走多远~!
这个搭建与“生产者角色”的搭建过程一样,只是在配置文件中需要修改部分内容。
该场景分为两个角色:生产者(商品系统)和消费者(商城系统),而商城系统就通过调用商品系统而查询到对应的商品信息而进行显示。(备注:简单演示消费者调用生产者的服务)
package com.scw.dubbo.first.client.service;
import com.scw.dubbo.first.client.response.BaseResponse;
public interface IDubboItemService {
/**
* 查询商品
* @return
*/
BaseResponse listItems();
}
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;
}
}
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();
}
id, code, name, price, is_active, create_time, update_time
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;
}
}
/*
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);
com.scw.dubbo.first
client
1.0.1
备注:其中的url如果是本地的话可以不写,因为在自测的时候都是在本的,而如果以后在实际项目后,我们可以设置生产者服务对应的服务器的IP地址和端口;
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;
}
PS:其实这个接口可以像上面的例子一样采取RPC的调用方式,但是为了让大家了解不同的调用方式,所以这个实例就采取Rest的调用方式。大家有兴趣的话,也可以将下面这个例子改成用RPC的形式哟。!
**备注:**下面的生产者和消费者,我就默认和上面的实例保持一样了,所以基本的环境搭建就不重复说,就只是说如何完成这个功能,如果环境还没搭好的,那么就认真看看上面的例子哟。
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;
数据库内容:
备注:大家主要是理解这个过程,而不是在于看数据库的结构设计,仅仅是一个示例;
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;
}
}
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);
}
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;
}
}
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 ;
}
}
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)场景如下:
原因分析: 这是因为当前的服务是调用方,并且调用的是controller层的请求,那么URL对应的上下文应该是系统服务,而不是dubbo的上下文。因此,应该将v2改为dubboSecond即可(具体可以对比下面的一种场景)。
(2)场景如下:
是否会怀疑到底是不是另外一个服务提供的服务呢?那我们回到IDEA则可以看到提供服务系统成功的打印了如下的信息,所以成功调用另外服务的rest接口啦!
哇塞。。。。。调用成功,基本流程已经打通,是不是觉得其实就和调用自己系统的方法一样呢?so easy!!!
调用post方法的优化
备注:上面调用的方式其实很简单,但是不够通用,为了系统能够更好的调用Post和Get类型的请求,我们应该封装一下,达到更好的优化效果!
(后续再补啦,一下接受太多不太好,大家赶紧先去试试感觉!!!!!!!!!!)