第19课:微服务综合实战(下)

第19课:微服务综合实战(下)

在第 18 课中,我们已经完成了下订单功能的开发。本文将为大家继续演示支付、微服务打包镜像,以及利用 Rancher 平台部署、管理容器等功能的开发。

支付功能

大家可以按照如下步骤新建 payment-service,完成支付功能。

1. 新建项目 payment-service,并在其 POM 文件中添加如下主要依赖:

<!-- Spring Data JPA 依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- MySQL 驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

2. 在该项目 resources 目录下新建配置文件 application.yml,并在配置文件中添加如下代码:

server:
  port: 8084
  context-path: /

spring:
  application:
    name: payment-service

  cloud:
    consul:
      ##注册中心 IP 地址
      host: localhost
      ##注册中心监听端口
      port: 8500
      discovery:
        register: true
        instance-id: payment-service

  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/db_payment?useSSL=false
    username: root
    password: root
    ##初始化连接数
    initialSize: 5
    ##最小连接数
    minIdle: 5
    ##最大连接数
    maxActive: 20
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

##调用微服务超时设置
ribbon:
  ReadTimeout: 6000
  SocketTimeout: 6000

##熔断超时设置
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000

management:
  health:
    consul:
      enabled: false


feign:
  remote:
    ##调用商品微服务名称
    commodity-service: commodity-service
    ##调用账户微服务名称
    account-service: account-service
    ##调用订单微服务名称
    order-service: order-service

在支付微服务中,需要调用商品、账户、订单微服务,这里建议将 Ribbon、Feign 的超时时间设置得长一些,否则容易出现调用超时异常。

3. 在程序启动主类 PaymentServiceApplication 上添加注解 @EnableDiscoveryClient、@EnableFeignClients,具体实现如下所示:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class PaymentServiceApplication {

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

注意,务必记得在主类上添加注解 @EnableDiscoveryClient,否则无法将支付微服务注册到注册中心,同时也不要忘记添加注解 @EnableFeignClients,否则 @FeignClient 相关接口无法生成动态代理并注入到 Spring 容器中。

4. 新建流水实体类 OrderTransInfo,具体代码如下:

@Table(name = "order_trans")
@Entity
public class OrderTransInfo {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "trans_id")
    private Integer transId;

    @Column(name = "order_id")
    private Integer orderId;

    @Column(name = "trans_count")
    private BigInteger transCount;

    @DateTimeFormat(pattern = "yyyy-MM-ddHH:mm:ss")
    @Column(name = "create_time")
    private Date createTime;

    @DateTimeFormat(pattern = "yyyy-MM-ddHH:mm:ss")
    @Column(name = "update_time")
    private Date updateTime;

    ... ## 此处省略 get set 方法
}

注意,需在该实体类上添加 @Entity、@Table 注解,否则在数据库中无法自动生成流水信息表。

5. 定义支付接口类 OrderTransService,并定义支付接口 makePayment,同时添加该接口实现类 OrderTransServiceImpl,实现类代码如下:

@Service("orderTransService")
public class OrderTransServiceImpl implements OrderTransService {
    @Autowired
    private OrderTransRepository orderTransDao;
    @Autowired
    private OrderFeignService orderFeignService;
    @Autowired
    private AccountFeignService accountFeignService;
    @Autowired
    private CommodityFeignService commodityFeignService;

    @Override
    public OrderTransInfo makePayment(Integer orderId, Integer userId) {
        OrderDto orderDto = getOrderDtoByOrderId(orderId);
        BigInteger purchaseTotalAmount = BigInteger.ZERO;

        if (orderDto != null) {
            if(orderDto.getPayStatus() == PubConstant.ORDER_HAS_PAID){
                throw new OrderHasPaidException("订单已经支付");
            }

            BigInteger commodityPrice = getCommodityPriceByCommodityId(orderDto.getCommodityId());
            //计算购买商品总金额
            purchaseTotalAmount = commodityPrice.multiply(new BigInteger(orderDto.getPurchaseCount() + ""));
        }

        //获取用户账户余额
        BigInteger accountBalance = getAccountBalanceByUserId(userId);
        if (accountBalance.compareTo(purchaseTotalAmount) < 0) {
            throw new AccountBalanceInsufficientException("用户账户余额不足");
        }

        //扣除用户账户余额
        accountFeignService.updateUserAccBalanceByUserId(userId, purchaseTotalAmount.negate());

        //修改订单状态
        orderFeignService.updateOrderStatus(orderId, PubConstant.ORDER_HAS_PAID);

        //创建订单流水
        OrderTransInfo orderTransInfo = createOrderTransInfo(orderId, purchaseTotalAmount);

        return orderTransInfo;
    }

    ... ## 由于篇幅问题,此处省略部分实现代码
}

注意,支付微服务通过 Feign 客户端调用商品、账户、订单等微服务接口,因为篇幅问题,此处省略了部分实现代码,读者可以下载课程源码进行学习。

6. 新建支付流水持久层接口定义类,代码如下:

/**
 * 订单交易持久层接口定义
 */
public interface OrderTransRepository extends JpaRepository<OrderTransInfo,Integer> {
}

7. 新建支付控制器类 OrderTransController,具体代码实现如下:

@RestController
@RequestMapping("/rest/orderTrans")
public class OrderTransController {
    @Autowired
    private OrderTransService orderTransService;


    /**
     * 调用支付服务
     * @param orderId
     * @param userId
     * @return
     */
    @RequestMapping("/makePayment")
    public ResultData makePayment(Integer orderId,Integer userId){
        ResultData resultData = new ResultData();
        resultData.setStatus(ResultData.Status.error);

        try{
            resultData.setBo(orderTransService.makePayment(orderId,userId));
            resultData.setStatus(ResultData.Status.success);
        }
        catch (OrderHasPaidException e){
            resultData.setMessage(e.getMessage());
        }
        catch (AccountBalanceInsufficientException e){
            resultData.setMessage(e.getMessage());
        }
        catch (Exception e){
            resultData.setMessage("订单交易服务调用异常");
        }

        return resultData;
    }
}

8. 顺序启动注册中心 Consul、账户微服务 account-service、网关 api-gateway、商品微服务 commodity-service、订单微服务 order-service、支付微服务 payment-service。启动完成之后,通过浏览器访问:http://localhost:8500,效果如下图所示:

enter image description here

9. 在演示订单、支付功能之前,首先需要在账户表中为某个用户添加一条账户数据,在商品表中添加一条商品数据,如下所示。

enter image description here

用户账户数据

enter image description here

商品数据

10. 打开 Postman,调用如下订单接口:

http://localhost:8080/order-api/rest/order/createOrder?userId=123&commodityId=123456&purchaseCount=3

结果如下图所示:

enter image description here

11. 根据上图返回的订单 ID 以及用户 ID 调用支付接口如下接口进行支付:

http://localhost:8080/payment-api/rest/orderTrans/makePayment?orderId=16&userId=123

调用结果如下:

enter image description here

最后观察数据库中商品信息表、账户表如下所示:

enter image description here

商品表

enter image description here

账户表

观察下订单、支付接口执行结果及数据库表信息,可知下订单、支付处理逻辑是正确的,详细实现请大家参看源码。

利用 Rancher 容器平台部署微服务

本功能的实现,我们分 10 个步骤来完成。

1. 利用 IDEA 的 Maven Package 将微服务 account-service、order-service、api-gateway、commodity-service、payment-service 打成对应的 Jar 包。

注意,为了正常部署,务必确保各个微服务中配置注册中心 IP 地址是 Consul 安装服务器 IP 地址。

2. 利用 FTP 将 Jar 上传到 Linux 服务器,如下图所示:

enter image description here

3. 在以上 Jar 包同目录下新建 Dockerfile 文件,并在该文件中添加如下代码,并保存:

##以java:8作为基础镜像
FROM java:8
VOLUME /tmp

##将api-gateway-0.0.1-SNAPSHOT.jar复制到容器中
ADD api-gateway-0.0.1-SNAPSHOT.jar /api-gateway-service-0.0.1-SNAPSHOT.jar

##对外暴露8080端口
EXPOSE 8080

##容器启动时执行命令 java -jar api-gateway-service-0.0.1-SNAPSHOT.jar
CMD ["java", "-jar", "/api-gateway-service-0.0.1-SNAPSHOT.jar"]

4. 执行命令生成镜像 api-gateway-service:1.0-SNAPSHOT:

docker build -t api-gateway-service:1.0-SNAPSHOT .

上面代码中,api-gateway-service:1.0-SNAPSHOT 表示生成镜像名称,“.”表示生成镜像上下文,不能省略。

做完第 3、4 步骤以后,镜像 api-gateway-service:1.0-SNAPSHOT 就完成了。篇幅有限,大家可以按照第 3、4 步骤独立完成商品、订单、支付、账户镜像的生成,这里就不再重复演示了。

5. 在 Linux 服务器上下载 Consul 安装文件,并执行命令启动 Consul 注册中心:

consul agent -server -ui -bootstrap-expect 1 -node=consul-server-microservice -data-dir ./consul-mic -datacenter=micro-test  -advertise 127.0.0.1 -client 0.0.0.0

此处省略了 Consul 下载过程,读者执行该命令之前请先下载 Consul 安装文件。

6. 创建 docker-compose.yml 服务编排文件,其详细内容如下:

version: "2.1"

services:
  version: "2"
services:
  api-gateway:
    image: api-gateway-service:1.0-SNAPSHOT
    environment:
      TZ: Asia/Shanghai
    ports:
      - "8080:8080/tcp"

  order-service:
    image: order-service:1.0-SNAPSHOT
    environment:
      TZ: Asia/Shanghai
    ports:
      - "8081:8081/tcp"

  account-service:
    image: account-service:1.0-SNAPSHOT
    environment:
      TZ: Asia/Shanghai
    ports:
      - "8082:8082/tcp"

  commodity-service:
    image: commodity-service:1.0-SNAPSHOT
    environment:
      TZ: Asia/Shanghai
    ports:
      - "8083:8083/tcp"

  payment-service:
    image: payment-service:1.0-SNAPSHOT
    environment:
      TZ: Asia/Shanghai
    ports:
      - "8084:8084/tcp"

该文件用于服务编排,其中 api-gateway、order-service、account-service、commodity-service、payment-service 表示服务名称,image 用来设置镜像名称,ports 用于将容器进程运行端口映射到宿主机端口,Docker 默认时区为 Etc/UTC,要让容器时间和宿主机显示时间一致,可将容器时区设置为 Asia/Shanghai。

7. 启动 Rancher 容器管理平台,并登录(Rancher 相关内容请参看 16 课),之后依次点击应用 -> 用户 -> 添加应用,在如下图所示的新界面中填写相关信息:

enter image description here

8. 点击上图所示的上传按钮,添加 docker-compose.yml 文件启动应用容器,容器启动后如下图所示:

enter image description here

9. 经过以上 8 个步骤,我们已经部署好了微服务容器。下面通过 Postman 调用如下订单接口:

http://192.168.1.121:8080/order-api/rest/order/createOrder?userId=123&commodityId=123456&purchaseCount=1

运行结果如下图所示,则说明订单创建成功:

enter image description here

10. 调用如下接口:

http://192.168.1.121:8080/payment-api/rest/orderTrans/makePayment?orderId=1&userId=123

进行订单支付,运行结果如下图所示:

enter image description here

大家可以点击如下网址,下载课程源码:

https://github.com/huangchaobing

到此,本达人课的内容就全部结束了。通过本课程内容的学习,希望大家都有所收获。由于时间关系,再加上自身水平有限,微服务的很多内容无法为大家一一呈现。你有任何问题欢迎通过读者圈或加我微信(715623229),与我交流。再次感谢大家对我的支持,谢谢!

上一篇
下一篇
目录