Loading

模拟微服务环境

1 数据库表

  • 用户表:
CREATE TABLE `tb_user` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`username` varchar(40) DEFAULT NULL COMMENT '用户名',
`password` varchar(40) DEFAULT NULL COMMENT '密码',
`age` int(3) DEFAULT NULL COMMENT '年龄',
`balance` decimal(10,2) DEFAULT NULL COMMENT '余额',
`address` varchar(80) DEFAULT NULL COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 商品表:
CREATE TABLE `tb_product` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`product_name` varchar(40) DEFAULT NULL COMMENT '名称',
`status` int(2) DEFAULT NULL COMMENT '状态',
`price` decimal(10,2) DEFAULT NULL COMMENT '单价',
`product_desc` varchar(255) DEFAULT NULL COMMENT '描述',
`caption` varchar(255) DEFAULT NULL COMMENT '标题',
`inventory` int(11) DEFAULT NULL COMMENT '库存',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

INSERT INTO `tb_product` VALUES (1, 'iPhone', 1, 5000.10, '苹果手机就是香', '苹果哇', 50);
  • 订单表:
CREATE TABLE `tb_order` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
`product_id` int(11) DEFAULT NULL COMMENT '商品id',
`number` int(11) DEFAULT NULL COMMENT '数量',
`price` decimal(10,2) DEFAULT NULL COMMENT '单价',
`amount` decimal(10,2) DEFAULT NULL COMMENT '总额',
`product_name` varchar(40) DEFAULT NULL COMMENT '商品名',
`username` varchar(40) DEFAULT NULL COMMENT '用户名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2 搭建环境

2.1 创建父工程spring_cloud_demo

  • 在IDEA中创建父工程spring_cloud_demo,并引入坐标:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>pom</packaging>
    <modules>
        <module>product_service</module>
        <module>spring_cloud_common</module>
        <module>order_service</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>


    <groupId>org.sunxiaping</groupId>
    <artifactId>spring_cloud_demo</artifactId>
    <version>1.0</version>


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>


    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>http://repo.spring.io/libs-snapshot-local</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>http://repo.spring.io/libs-milestone-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-releases</id>
            <name>Spring Releases</name>
            <url>http://repo.spring.io/libs-release-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>http://repo.spring.io/libs-snapshot-local</url>
            <snapshots>
                <enabled>true</enabled>
            </snapshots>
        </pluginRepository>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>http://repo.spring.io/libs-milestone-local</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
    
</project>

2.2 创建微服务工程模块

  • 创建商品微服务模块spring_cloud_product。
  • 创建订单微服务模块spring_cloud_order。

3 搭建商品微服务

3.1 在pom.xml中导入相关jar包的坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>org.sunxiaping</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>product_service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.2.2.RELEASE</version>
                <configuration>
                    <fork>true</fork>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

3.2 编写实体类

  • Product.java
package com.sunxiaping.product.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "tb_product")
public class Product implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "product_name")
    private String productName;

    @Column(name = "status")
    private Integer status;

    @Column(name = "price")
    private BigDecimal price;

    @Column(name = "product_desc")
    private String productDesc;

    @Column(name = "caption")
    private String caption;

    @Column(name = "inventory")
    private String inventory;
    
}

3.3 编写dao层

  • ProductDao.java
package com.sunxiaping.product.dao;

import com.sunxiaping.product.domain.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {

}

3.4 编写service层

  • ProductService.java
package com.sunxiaping.product.service;

import com.sunxiaping.product.domain.Product;

public interface ProductService {

    /**
     * 根据id查询
     *
     * @param id
     * @return
     */
    Product findById(Long id);


    /**
     * 保存
     *
     * @param product
     */
    void save(Product product);


    /**
     * 更新
     *
     * @param product
     */
    void update(Product product);


    /**
     * 删除
     *
     * @param id
     */
    void delete(Long id);

}
  • ProductServiceImpl.java
package com.sunxiaping.product.service.impl;

import com.sunxiaping.product.dao.ProductRepository;
import com.sunxiaping.product.domain.Product;
import com.sunxiaping.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;

@Service
@Transactional
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Override
    public Product findById(Long id) {
        return productRepository.findById(id).orElse(new Product());
    }

    @Override
    public void save(Product product) {
        productRepository.save(product);
    }

    @Override
    public void update(Product product) {
        productRepository.save(product);
    }

    @Override
    public void delete(Long id) {
        productRepository.deleteById(id);
    }
}

3.5 编写controller层

  • ProductController.java
package com.sunxiaping.product.controller;

import com.sunxiaping.product.domain.Product;
import com.sunxiaping.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(value = "/product")
public class ProductController {

    @Autowired
    private ProductService productService;

    @PostMapping(value = "/save")
    public String save(@RequestBody Product product) {
        productService.save(product);
        return "新增成功";
    }

    @GetMapping(value = "/findById/{id}")
    public Product findById(@PathVariable(value = "id") Long id) {
        return productService.findById(id);
    }
}

3.6 配置yml文件

  • application.yml
server:
  port: 9001 # 微服务的端口号

spring:
  application:
    name: service-product # 微服务的名称
  datasource:
    url: jdbc:mysql://192.168.237.100:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
  jpa:
    generate-ddl: true
    show-sql: true
    open-in-view: true
    database: mysql

3.7 配置启动类

  • ProductApplication.java
package com.sunxiaping.product;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;

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

4 搭建订单微服务

4.1 在pom.xml中导入相关jar包的坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>org.sunxiaping</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>order_service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>

</project>

4.2 编写实体类

  • Product.java
package com.sunxiaping.order.domain;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;
import java.io.Serializable;
import java.math.BigDecimal;

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "tb_product")
public class Product implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "product_name")
    private String productName;

    @Column(name = "status")
    private Integer status;

    @Column(name = "price")
    private BigDecimal price;

    @Column(name = "product_desc")
    private String productDesc;

    @Column(name = "caption")
    private String caption;

    @Column(name = "inventory")
    private String inventory;
    
}

4.3 配置yml

  • application.yml
server:
  port: 9002 # 微服务的端口号

spring:
  application:
    name: service-order # 微服务的名称
  datasource:
    url: jdbc:mysql://192.168.237.100:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: 123456
  jpa:
    generate-ddl: true
    show-sql: true
    open-in-view: true
    database: mysql

4.4 配置启动类

  • OrderApplication.java
package com.sunxiaping.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

4.5 服务调用

4.5.1 概述

  • 在用户下单的时候需要调用商品微服务获取商品数据,那么应该需要怎么做呢?商品微服务提供了供人调用的HTTP接口,所以下订单的时候使用HTTP请求的相关工具类完成,如常见的HttpClient、OkHttp,当然也可以使用Spring提供的RestTemplate。

4.5.2 RestTemplate介绍

  • Spring框架提供的RestTemplate类可用于在应用中调用RESTful服务,它简化了和HTTP服务的通信方式,统一了RESTful的标准,封装了HTTP的链接,我们只需要传入URL和返回值类型即可。相较于之前常用的HttpClient、OkHttp等,RestTemplate是一种更加优雅的调用RESTful服务的方式。

  • 在Spring应用程序中访问第三方REST服务和Spring的RestTemplate类有关。RestTemplate类的设计原则和许多其他Spring模块类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。

  • RestTemplate默认依赖JDK提供HTTP连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为如Apache HttpComponents、Netty或OkHttp等其他HTTP库。

  • 考虑到RestTemplate类是为调用REST服务而设计的,因此他的主要方法和REST的基础紧密相连就不足为奇,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate类具有headForHeaders()、getForObject()等方法。

4.6 向Spring容器中注册RestTemplate

  • SpringConfig.java
package com.sunxiaping.order.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class SpringConfig {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

4.7 编写下单方法

  • OrderController.java
package com.sunxiaping.order.controller;

import com.sunxiaping.product.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
@RequestMapping(value = "/order")
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 通过订单系统,调用商品微服务根据id查询商品信息
     *
     * @param id
     * @return
     */
    @GetMapping(value = "/buy/{id}")
    public Product buy(@PathVariable(value = "id") Long id) {

        Product product = restTemplate.getForObject("http://localhost:9001/product/findById/" + id, Product.class);

        return product;
    }
}

5 分析模拟微服务中存在的问题

  • 1️⃣服务调用者将微服务的请求路径硬编码到Java代码中。
  • 2️⃣不能​对微服务负载均衡的调用,因为请求路径的URL硬编码了。
  • 3️⃣服务多起来,对前端调用不友好,需要加入API网关。
  • 4️⃣微服务多起来的话,如果每次都需要重新修改配置文件,很麻烦,需要配置的统一管理。
  • 5️⃣链路追踪。
  • 6️⃣系统容错。
  • 7️⃣……

分析模拟微服务中存在的问题

posted @ 2020-09-24 20:44  许大仙  阅读(336)  评论(3编辑  收藏  举报