Spring Cloud的服务注册和服务调用(服务通信)
前提条件:
在编写模块数据调用的java代码之前,需要先把nacos先从git上down下来,并且run起来
安装了docker的前提下执行以下命令:
操作步骤:
- Clone 项目
git clone https://github.com/nacos-group/nacos-docker.git
cd nacos-docker
- 单机模式 Derby
docker-compose -f example/standalone-derby.yaml up
- 单机模式 Mysql
docker-compose -f example/standalone-mysql.yaml up
- 集群模式
docker-compose -f example/cluster-hostname.yaml up
以上三种启动模式根据自己的实际需求选择不同的启动模式,详细请参考
https://nacos.io/zh-cn/docs/quick-start-docker.html
一、导入相关依赖
提供服务(数据)的模块导入discovery依赖、调用服务模块两个依赖均导入
<properties>
<java.version>1.8</java.version>
<discovery.version>0.9.0.RELEASE</discovery.version>
<openfeign.version>2.1.3.RELEASE</openfeign.version>
</properties>
<dependencies>
<!--==服务注册依赖==-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>${discovery.version}</version>
</dependency>
<!--==服务通信依赖(需要去获取其他模块的数据的时候导入)-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>${openfeign.version}</version>
</dependency>
</dependencies>
二、配置application.yml文件
服务提供模块和服务调用模块均只需少量配置即可达到通信功能
application.yml 两个模块均适用(注意使用不同的端口号),因为只需要改端口号,这里就先只写一份yml了,端口号自己去改
server:
port: 9090
# 服务注册中心
spring:
# 服务名
application:
name: user-center-zsm
cloud:
nacos:
discovery:
server-addr: ip:8848
三、编写服务提供方和服务调用方两个服务的java代码
这里我就以订单服务和用户服务为例子了:
user模块调用order模块的数据,我这里因为order模块还调用了其他模块的数据,因此也开启了服务通信(伪数据实现、主要讲用法)
order模块结构:
首先第一步现在Springboot项目的主程序入口开启服务注册和服务通信
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
//开启服务注册
@EnableDiscoveryClient
//开启服务通信
@EnableFeignClients
public class SpringcloudOrderCenterApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudOrderCenterApplication.class, args);
}
}
Service
import com.springcloud.alibaba.springcloudordercenter.domain.dto.OrderDTO;
public interface OrderService {
OrderDTO getOrderData(Integer oid);
}
ServiceImpl(这里我组装了其他模块的数据,可以忽略、只需要注意返回的是OrderDTO)
import com.springcloud.alibaba.springcloudordercenter.domain.dto.AliPayDTO;
import com.springcloud.alibaba.springcloudordercenter.domain.dto.OrderDTO;
import com.springcloud.alibaba.springcloudordercenter.domain.dto.PayInfo;
import com.springcloud.alibaba.springcloudordercenter.domain.dto.WeChatPayDTO;
import com.springcloud.alibaba.springcloudordercenter.service.AliPayInfoService;
import com.springcloud.alibaba.springcloudordercenter.service.OrderService;
import com.springcloud.alibaba.springcloudordercenter.service.WeChatPayInfoService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @Author zsm
* @Service 业务逻辑层, 查询订单相关信息,返回OrderDTO伪数据
*
*/
//spring的service
@Service
public class OrderServiceImpl implements OrderService {
@Resource
AliPayInfoService aliPayInfoService;
@Resource
WeChatPayInfoService weChatPayInfoService;
@Override
public OrderDTO getOrderData(Integer oid) {
//分别获取Ali支付和微信支付的数据
AliPayDTO aliPayInfo = aliPayInfoService.getAliPayInfo(1);
WeChatPayDTO weChatPayInfo = weChatPayInfoService.getWeChatPayInfo(1);
OrderDTO orderDTO = new OrderDTO();
orderDTO.setOid(1);
orderDTO.setOrderNo("1234567");
orderDTO.setPayType(1);
//如果支付类型是1,那么支付信息就是Ali的,如果支付信息是除了1以外的数字支付信息就是微信的
if (orderDTO.getPayType()==2){
PayInfo<AliPayDTO> aliPayDTOPayInfo = new PayInfo<>();
aliPayDTOPayInfo.setPayInfo(aliPayInfo);
orderDTO.setPayInfo(aliPayDTOPayInfo);
}else {
PayInfo<WeChatPayDTO> weChatPayDTOPayInfo = new PayInfo<>();
weChatPayDTOPayInfo.setPayInfo(weChatPayInfo);
orderDTO.setPayInfo(weChatPayDTOPayInfo);
}
return orderDTO;
}
}
Controller
import com.springcloud.alibaba.springcloudordercenter.domain.dto.OrderDTO;
import com.springcloud.alibaba.springcloudordercenter.service.OrderService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* Controller层 对外暴露数据接口
*/
@RestController
@RequestMapping("/orders")
public class OrderController {
@Resource
OrderService orderService;
@RequestMapping("/getOrderData/{oid}")
public OrderDTO getOrderData(@PathVariable Integer oid) {
OrderDTO orderData = orderService.getOrderData(oid);
return orderData;
}
}
以上就是服务提供方模块提供order相关的数据。
==================================================================
user模块结构
第一步也是在主程序入口开启服务注册和服务通信
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
//开启服务注册
@EnableDiscoveryClient
//开启服务发现
@EnableFeignClients
public class SpringcloudUserCenterApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudUserCenterApplication.class, args);
}
}
OrderDTO(order模块Controller层返回的数据类型)
import lombok.Data;
/**
* Order 数据传输对象
*/
@Data
public class OrderDTO {
//Order 主键
Integer oid;
//订单号
String orderNo;
//支付方式
Integer payType;
//支付信息
PayInfo payInfo;
}
UserVO(user模块传递给前端需要展示的数据)
import com.springcloud.alibaba.springcloudusercenter.domain.dto.OrderDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 返回给前端界面展示的数据
*
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserVO {
//User 主键id
Integer uid;
//User 用户名
String username;
//组装OrderDTO
OrderDTO orderDTO;
//2个参数的构造
public UserVO(Integer uid,String username){
this.uid = uid;
this.username = username;
}
}
OrderServie: 获取order模块数据
import com.springcloud.alibaba.springcloudusercenter.domain.dto.OrderDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 获取Order层的数据
*
* @FeignClient http协议通信 value的值是order模块yml文件中配置的服务名
* @Service dubbo gRPC通信
*/
@FeignClient(value = "order-center-zsm")
public interface OrderService {
//order模块Controller层暴露出来的urlMapping地址
@RequestMapping("/orders/getOrderData/{oid}")
OrderDTO getOrderData(@PathVariable Integer oid);
}
UserService:
import com.springcloud.alibaba.springcloudusercenter.domain.vo.UserVO;
public interface UserService {
UserVO getUserData(Integer uid);
}
UserServiceImpl(这一层组装数据)
import com.springcloud.alibaba.springcloudusercenter.domain.dto.OrderDTO;
import com.springcloud.alibaba.springcloudusercenter.domain.vo.UserVO;
import com.springcloud.alibaba.springcloudusercenter.service.OrderService;
import com.springcloud.alibaba.springcloudusercenter.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @Author zsm
* @Service 业务逻辑层,返回UserVO给前端
* 业务层组装数据
*/
@Service
public class UserServiceImpl implements UserService {
@Resource
OrderService orderService;
@Override
public UserVO getUserData(Integer uid) {
//传个伪数据
OrderDTO orderData = orderService.getOrderData(1);
//new UserVO
UserVO userInfo = new UserVO(1, "张顺民");
userInfo.setOrderDTO(orderData);
return userInfo;
}
}
UserController:
import com.springcloud.alibaba.springcloudusercenter.domain.vo.UserVO;
import com.springcloud.alibaba.springcloudusercenter.service.UserService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* Controller层,暴露数据接口给其他模块或前端调用
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
UserService userService;
@RequestMapping("/getUserData/{uid}")
public UserVO getUserData(@PathVariable Integer uid) {
UserVO userData = userService.getUserData(uid);
return userData;
}
}
common包下全局结果集处理等
ResponseResult(全局返回结果集类型)
/**
* 返回数据的全局类型
* @param <T>
*/
//启用构建者模式
@Data
@Builder
public class ResponseResult<T> {
//响应状态码
private Integer statusCode;
//响应提示信息
private String tipMsg;
//全局返回数据类型
private T data;
public static <T> ResponseResult success(T data){
return ResponseResult.builder().statusCode(ResponseStatus.SUCCESS.getStatusCode())
.tipMsg(ResponseStatus.SUCCESS.getTipsMsg())
.data(data).build();
}
public static <T> ResponseResult fail(){
return ResponseResult.builder().statusCode(ResponseStatus.FAIL_404.getStatusCode())
.tipMsg(ResponseStatus.FAIL_404.getTipsMsg())
.build();
}
}
全局结果集处理类ResponseAdvice
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
/**
* 全局结果集处理
*/
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
// 是否执行第二个方法
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
//Object为Controller层返回的对象类型
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
ResponseResult result = ResponseResult.success(o);
return result;
}
}
ResponseSatus状态码、提示信息等常量值的枚举类
/**
* 全局影响信息枚举类
*/
public enum ResponseStatus {
SUCCESS(20000,"获取数据成功"),
FAIL_404(40004,"网络不给力,请稍后重试");
private Integer statusCode;
private String tipsMsg;
ResponseStatus(Integer statusCode, String tipsMsg) {
this.statusCode = statusCode;
this.tipsMsg = tipsMsg;
}
public Integer getStatusCode() {
return statusCode;
}
public void setStatusCode(Integer statusCode) {
this.statusCode = statusCode;
}
public String getTipsMsg() {
return tipsMsg;
}
public void setTipsMsg(String tipsMsg) {
this.tipsMsg = tipsMsg;
}
}