一、什么是微服务
微服务是由单一应用程序构成的小服务,自己拥有自己的行程与轻量化处理,服务依业务功能设计,以全自动的方式部署,与其他服务使用HTTP API通信。同时服务会使用最小的模的集中管理能力,服务可以用不同的编程语言与数据库等组件实现。
简单来说微服务就是将一个大型项目的各个业务代码拆分成多个互不干扰的小项目,而这些小项目专心完成自己的功能,而且可以调用别的小项目的方法,从而完成整体的功能。
二、Spring Cloud
SpringCloud是由Spring提供的一套能够快速搭建微服务架构程序的框架集,它不是一个框架而是很多框架的统称,它的目的就是为了搭建微服务架构。
Spring Cloud的提供者主要有:
1.Spring自己编写的框架和软件
2.Netflix(奈非):早期提供了很多(全套)微服务架构组件
3.alibaba(阿里巴巴):新版本SpringCloud,推荐使用(正在迅速占领市场)
从功能上微服务的功能可以细分为以下几个方面:
1.微服务的注册中心
2.微服务间的调用
3.微服务的分布式事务
4.微服务的限流
5.微服务的网关
三、Nacos
Nacos是Spring Cloud Alibaba提供的一个软件,主要有注册中心和配置中心的功能。微服务的统筹管理依赖于这个注册中心,像组件服务器托管网化的注册那样,只有注册了才能成为微服务的一部分。
下载地址:https://github.com/alibaba/nacos/releases/download/1.4.2/nacos-server-1.4.2.zip
下载完成之后我们进入到bin目录,执行cmd命令startup.cmd -m standalone
,其中-m standalone指定为单机模式,否则以cluster集群模式启动。
我们访问nacos的控制台地址:http://192.168.248.1:8848/nacos/index.html
nacos控制台的初始账号和密码都是nacos。我们登录成功之后的nacos控制台如下所示:
我们的nacos登录成功之后窗口不能关闭,否则服务将可能关闭导致无法注册到nacos后台,微服务就没办法使用。
四、创建微服务项目-公共模块
1.创建父项目
不需要勾选其他依赖:
创建项目成功之后我们删除其他目录,只保留一个pom文件即可。
完整的pom文件如下所示:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.7.16
com.example
MyCloud
0.0.1-SNAPSHOT
MyCloud
MyCloud
1.8
2020.0.3
2.2.2.RELEASE
2.5.4
2.3.0.RELEASE
1.0.10.RELEASE
2.2.0
3.4.1
1.4.0
8.0.26
1.18.20
2.0.9
2.3.10
5.5.2
1.2.45
1.1.20
0.9.0
1.4.2
pom
io.seata
seata-all
${seata-server.version}
org.projectlombok
lombok
${lombok.version}
mysql
mysql-connector-java
${mysql.version}
runtime
com.alibaba
druid
${druid.version}
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybatis-spring-boot.version}
com.baomidou
mybatis-plus-boot-starter
${mybaits-plus.version}
com.baomidou
mybatis-plus-generator
${mybaits-plus.version}
com.github.pagehelper
pagehelper-spring-boot-starter
${pagehelper-spring-boot.version}
org.springframework.boot
spring-boot-starter
${spring-boot.version}
org.springframework.boot
spring-boot-starter-web
${spring-boot.version}
org.springframework.boot
spring-boot-starter-freemarker
${spring-boot.version}
org.springframework.boot
spring-boot-starter-validation
${spring-boot.version}
org.springframework.boot
spring-boot-starter-security
${spring-boot.version}
org.springframework.boot
spring-boot-starter-oauth2-client
${spring-boot.version}
org.springframework.boot
spring-boot-configuration-processor
${spring-boot-configuration-processor.version}
org.springframework.security
spring-security-jwt
${spring-security-jwt.version}
com.github.xiaoymin
knife4j-spring-boot-starter
${knife4j-spring-boot.version}
org.springframework.boot
spring-boot-starter-data-redis
${spring-boot.version}
org.springframework.boot
spring-boot-starter-data-mongodb
${spring-boot.version}
org.springframework.boot
spring-boot-starter-data-elasticsearch
${spring-boot.version}
org.springframework.boot
spring-boot-starter-amqp
${spring-boot.version}
org.springframework.boot
spring-boot-starter-actuator
${spring-boot.version}
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${spring-cloud-alibaba.version}
pom
import
com.alibaba
fastjson
${fastjson.version}
io.jsonwebtoken
jjwt
${jjwt.version}
org.springframework.boot
spring-boot-starter-test
${spring-boot.version}
test
org.springframework.amqp
spring-rabbit-test
${spring-rabbit-test.version}
test
org.springframework.security
spring-security-test
${spring-security-test.version}
test
io.seata
seata-spring-boot-starter
${seata-server.version}
org.springframework.boot
spring-boot-maven-plugin
2.创建通用子模块
选中父项目,右键新建一个Module。
创建子模块成功之后,完整的pom文件如下所示:
4.0.0
com.example
MyCloud
0.0.1-SNAPSHOT
cloud-commons
8
8
UTF-8
com.github.xiaoymin
knife4j-spring-boot-starter
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-json
org.springframework.boot
spring-boot-starter-tomcat
购物车相关代码:
package com.example.pojo.cart.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author qx
* @date 2023/9/25
* @des 购物车新增DTO
*/
@ApiModel("购物车新增DTO")
@Data
public class CartAddDTO implements Serializable {
@ApiModelProperty(name = "commodityCode", value = "商品编号")
private String commodityCode;
@ApiModelProperty(name = "price", value = "商品单价")
private Double price;
@ApiModelProperty(name = "count", value = "商品数量")
private Integer count;
@ApiModelProperty(name = "userId", value = "用户ID")
private Long userId;
}
package com.example.pojo.cart.entity;
import lombok.Data;
import java.io.Serializable;
/**
* @author qx
* @date 2023/9/25
* @des 购物车实体类
*/
@Data
public class Cart implements Serializable {
private Long id;
private String commodityCode;
private Double price;
private Integer count;
private Long userId;
}
订单相关代码:
package com.example.pojo.order.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author qx
* @date 2023/9/25
* @des 新增订单DTO
*/
@ApiModel("新增订单的DTO")
@Data
public class OrderAddDTO implements Serializable {
@ApiModelProperty(name = "userId", value = "用户Id")
private Long userId;
@ApiModelProperty(name = "commodityCode", value = "商品编号")
private String commodityCode;
@ApiModelProperty(name = "count", value = "商品数量")
private Integer count;
@ApiModelProperty(name = "money", value = "总金额")
private Double money;
}
package com.example.pojo.order.entity;
import lombok.Data;
import java.io.Serializable;
/**
* @author qx
* @date 2023/9/25
* @des 订单实体类
*/
@Data
public class Order implements Serializable {
private Long id;
/**
* 用户ID
*/
private String userId;
/**
* 商品编号
*/
private String commodityCode;
/**
* 商品数量
*/
private Integer count;
/**
* 总金额
*/
private Double money;
}
库存相关代码:
package com.example.pojo.stock.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* @author qx
* @date 2023/9/25
* @des 商品减少库存DTO
*/
@ApiModel("商品减少库存DTO")
@Data
public class StockReduceCountDTO implements Serializable {
@ApiModelProperty(name = "commodityCode", value = "商品编号")
private String commodityCode;
@ApiModelProperty(name = "reduceCount", value = "减库存数")
private Integer reduceCount;
}
package com.example.pojo.stock.entity;
import lombok.Data;
import java.io.Serializable;
/**
* @author qx
* @date 2023/9/25
* @des 库存实体类
*/
@Data
public class Stock implements Serializable {
private Long id;
/**
* 商品编码
*/
private String commodityCode;
/**
* 减库存数
*/
private Integer reduceCount;
}
返回数据格式类创建:
错误代码枚举类型代码如下:
package com.example.common.restful;
import lombok.Getter;
/**
* @author qx
* @date 2023/9/25
* @des 错误代码枚举类型
*/
@Getter
public enum ResponseCode {
OK(200), BAD_REQUEST(400), UNAUTHORIZED(401), FORBIDDEN(403), NOT_FOUND(404), NOT_ACCEPTABLE(406), CONFLICT(409), INTERNAL_SERVER_ERROR(500);
private final Integer code;
ResponseCode(Integer code) {
this.code = code;
}
}
自定义异常代码如下:
package com.example.common.exception;
import com.example.common.restful.ResponseCode;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author qx
* @date 2023/9/25
* @des 自定义异常
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class CloudServiceException extends RuntimeException {
private ResponseCode responseCode;
public CloudServiceException(ResponseCode responseCode, String message) {
super(message);
setResponseCode(responseCode);
}
}
全局返回数据类代码如下:
package com.example.common.restful;
import com.example.common.exception.CloudServiceException;
import lombok.Data;
import java.io.Serializable;
/**
* @author qx
* @date 2023/9/25
* @des 全局返回数据处理类
*/
@Data
public class JsonResult implements Serializable {
/**
* 状态码
*/
private Integer state;
/**
* 消息
*/
private String message;
/**
* 数据
*/
private T data;
/**
* 创建响应结果对象,表示"成功",不封装其它任何数据
*
* @return 响应结果对象
*/
public static JsonResult ok() {
return ok("OK");
}
public static JsonResult ok(String message) {
JsonResult jsonResult = new JsonResult();
jsonResult.setState(ResponseCode.OK.getCode());
jsonResult.setMessage(message);
jsonResult.setData(null);
return jsonResult;
}
/**
* 创建响应结果对象,表示"成功",且封装客户端期望响应的数据
*
* @param data 客户端期望响应的数据
* @return 响应结果对象
*/
public static JsonResult ok(String message, T data) {
JsonResult jsonResult = new JsonResult();
jsonResult.setState(ResponseCode.OK.getCode());
jsonResult.setData(da服务器托管网ta);
return jsonResult;
}
/**
* 创建响应结果对象,表示"失败",且封装"失败"的描述
*
* @param e CoolSharkServiceException异常对象
* @return 响应结果对象
*/
public static JsonResult failed(CloudServiceException e) {
return failed(e.getResponseCode(), e);
}
/**
* 创建响应结果对象,表示"失败",且封装"失败"的描述
*
* @param responseCode "失败"的状态码
* @param e "失败"时抛出的异常对象
* @return 响应结果对象
*/
public static JsonResult failed(ResponseCode responseCode, Throwable e) {
return failed(responseCode, e.getMessage());
}
/**
* 创建响应结果对象,表示"失败",且封装"失败"的描述
*
* @param responseCode "失败"的状态码
* @param message "失败"的描述文本
* @return 响应结果对象
*/
public static JsonResult failed(ResponseCode responseCode, String message) {
JsonResult jsonResult = new JsonResult();
jsonResult.setState(responseCode.getCode());
jsonResult.setMessage(message);
return jsonResult;
}
}
全局异常处理类代码如下:
package com.example.common.exception;
import com.example.common.restful.JsonResult;
import com.example.common.restful.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* @author qx
* @date 2023/9/25
* @des 全局异常处理类
*/
@RestControllerAdvice
@Slf4j
public class GlobalControllerExceptionHandler {
/**
* 处理绑定异常(通过Validation框架验证请求参数时的异常)
*/
@ExceptionHandler(BindException.class)
public JsonResult handleBindException(BindException e) {
String message = e.getBindingResult().getFieldError().getDefaultMessage();
JsonResult result = JsonResult.failed(ResponseCode.BAD_REQUEST, message);
log.debug("返回:{}", result);
return result;
}
/**
* 处理系统(其它)异常
*/
@ExceptionHandler({Throwable.class})
public JsonResult handleSystemError(Throwable e) {
JsonResult result = JsonResult.failed(ResponseCode.INTERNAL_SERVER_ERROR, e);
log.debug("返回:{}", result);
return result;
}
/**
* 处理业务异常
*/
@ExceptionHandler({CloudServiceException.class})
public JsonResult handleCloudServiceException(CloudServiceException e) {
JsonResult result = JsonResult.failed(e);
log.debug("返回:{}", result);
return result;
}
}
五、创建业务子模块
创建子模块
完整的pom文件如下所示:
4.0.0
com.example
MyCloud
0.0.1-SNAPSHOT
cloud-bussiness
8
8
UTF-8
org.springframework.boot
spring-boot-starter-web
com.github.xiaoymin
knife4j-spring-boot-starter
com.example
cloud-commons
0.0.1-SNAPSHOT
接着创建Knife4jConfiguration,这是在线文档的配置:
package com.example.bussiness.config;
import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {
/**
* 【重要】指定Controller包路径
*/
private String basePackage = "com.example.bussiness.controller";
/**
* 分组名称
*/
private String groupName = "base-bussiness";
/**
* 主机名
*/
private String host = "xxxxxxxxxx";
/**
* 标题
*/
private String title = "bussiness-web实例";
/**
* 简介
*/
private String description = "bussiness-web项目,实现购买";
/**
* 服务条款URL
*/
private String termsOfServiceUrl = "http://www.apache.org/licenses/LICENSE-2.0";
/**
* 联系人
*/
private String contactName = "codingfire";
/**
* 联系网址
*/
private String contactUrl = "https://blog.csdn.net";
/**
* 联系邮箱
*/
private String contactEmail = "xx@163.com";
/**
* 版本号
*/
private String version = "1.0-SNAPSHOT";
@Autowired
private OpenApiExtensionResolver openApiExtensionResolver;
@Bean
public Docket docket() {
String groupName = "1.0-SNAPSHOT";
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.host(host)
.apiInfo(apiInfo())
.groupName(groupName)
.select()
.apis(RequestHandlerSelectors.basePackage(basePackage))
.paths(PathSelectors.any())
.build()
.extensions(openApiExtensionResolver.buildExtensions(groupName));
return docket;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(title)
.description(description)
.termsOfServiceUrl(termsOfServiceUrl)
.contact(new Contact(contactName, contactUrl, contactEmail))
.version(version)
.build();
}
}
业务代码:
package com.example.bussiness.service;
/**
* @author qx
* @date 2023/9/25
* @des
*/
public interface IBusinessService {
void buy();
}
package com.example.bussiness.service.impl;
import com.example.bussiness.service.IBusinessService;
import com.example.common.pojo.order.dto.OrderAddDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* @author qx
* @date 2023/9/25
* @des
*/
@Slf4j
@Service
public class BusinessServiceImpl implements IBusinessService {
@Override
public void buy() {
OrderAddDTO orderAddDTO = new OrderAddDTO();
orderAddDTO.setCommodityCode("PC001");
orderAddDTO.setUserId(2L);
orderAddDTO.setMoney(500.0);
orderAddDTO.setCount(5);
log.info("新增订单信息为:{}", orderAddDTO);
}
}
控制层:
package com.example.bussiness.controller;
import com.example.bussiness.service.IBusinessService;
import com.example.common.restful.JsonResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author qx
* @date 2023/9/25
* @des 控制层
*/
@RestController
@RequestMapping("/bussiness")
public class BussinessController {
@Autowired
private IBusinessService businessService;
/**
* 购买
*
* @return
*/
@PostMapping("/buy")
public JsonResult buy() {
businessService.buy();
return JsonResult.ok();
}
}
注册业务类到nacos
我们还需要在bussiness的pom文件中添加如下依赖:
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
修改application.yml配置如下:
server:
port: 20000
spring:
# 当前Springboot项目的名称,用于注册中心服务的名称
application:
name: nacos-business
cloud:
nacos:
discovery:
#定义nacos运行的路径
server-addr: localhost:8848
重新运行程序,我们查看nacos控制台发现控制台的服务管理存在我们刚才创建的服务。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
近年来,虚拟现实(Virtual Reality, VR)技术在市场上的应用越来越广泛,虚拟现实已成为一个热门的科技话题。相关数据显示,2019年至2021年,我国虚拟现实市场规模不断扩大,从2019年的282.8亿元增长至2021年的583.9亿元,市场规模…