(三)SpringCloud学习笔记之案例搭建


使用微服务架构的分布式系统,微服务之间通过网络通信。我们通过服务提供者与服务消费者来描述微服务间的调用关系。

  • 服务提供者:服务的被调用方,提供调用接口的一方
  • 服务消费者:服务的调用方,依赖于其他服务的一方

我们以电商系统中常见的用户下单为例,用户向订单微服务发起一个购买的请求。在进行保存订单之前需要调用商品微服务查询当前商品库存,单价等信息。在这种场景下,订单微服务就是一个服务消者,商品微服务就是一个服务提供者
在这里插入图片描述

1 创建数据库表

1.1 用户表

CREATE TABLE `tb_user` (
 `id` int(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

1.2 商品表

CREATE TABLE `tb_product` (
 `id` int(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

1.3 订单表

CREATE TABLE `tb_order` (
 `id` int(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
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

2 环境搭建

2.1 创建父工程shop_parent,并引入坐标

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

    <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-logging</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>Greenwich.RELEASE</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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

2.2 创建微服务工程模块,并引入坐标

  • 创建订单微服务模块 order_service
  • 创建商品微服务模块 product_service
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.32</version>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>

3 搭建商品微服务

3.1 编写实体类

在product_service中创建 cn.itcast.entity.Product 实体类

/**
 * 商品实体类
 */
@Data //这里使用了lombok简化实体类的开发 Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率
@Entity
@Table(name = "tb_product")
public class Product {
    @Id
    private Long id;
    private String productName;
    private Integer status;
    private BigDecimal price;
    private String productDesc;
    private String caption;
}

3.2 编写dao接口

在product_service中创建 cn.itcast.entity.ProductDao 接口

public interface ProductDao extends JpaRepository<Product,Long>, JpaSpecificationExecutor<Product> {
}

3.3 编写service层

在product_service中创建 cn.itcast.entity.ProductService 接口

public interface ProductService {
    //根据id查询
    Product findById(Long id);
    //查询全部
    List findAll();
    //保存
    void save(Product product);
    //更新
    void update(Product product);
    //删除
    void delete(Long id);
}

在product_service中创建 cn.itcast.product.service.ProductServiceImpl 实现类

@Service("productService")
public class ProductServiceImpl implements ProductService {
    @Autowired
    private ProductDao productDao;
    @Override
    public Product findById(Long id) {
        return productDao.findById(id).get();
    }
    @Override
    public List findAll() {
        return productDao.findAll();
    }
    @Override
    public void save(Product product) {
        productDao.save(product);
    }
    @Override
    public void update(Product product) {
        productDao.save(product);
    }
    @Override
    public void delete(Long id) {
        productDao.deleteById(id);
    }
}

3.4 编写web层

在product_service中创建 cn.itcast.product.controller.ProductController

@RestController
@RequestMapping("/product")
public class ProductController {
    @Autowired
    private ProductService productService;
    @GetMapping
    public List findAll() {
        return productService.findAll();
    }
    @GetMapping("/{id}")
    public Product findById(@PathVariable Long id) {
        return productService.findById(id);
    }
    @PostMapping
    public String save(@RequestBody Product product) {
        productService.save(product);
        return "保存成功";
    }
    @PutMapping("/{id}")
    public String update(@RequestBody Product product) {
        productService.update(product);
        return "修改成功";
    }
    @DeleteMapping("/{id}")
    public String delete(Long id) {
        productService.delete(id);
        return "删除成功";
    }
}

3.5 配置启动类

在product_service中创建 cn.itcast.product.ProductApplication 启动类


@SpringBootApplication
@EntityScan("cn.itcast.product.entity")
public class ProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class);
    }
}

3.6 配置yml文件

server:
  port: 9001
spring:
  application:
    name: service-product
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8
    username: root
    password: 1234
  jpa:
    database: MySQL
    show-sql: true
    open-in-view: true

4 搭建订单微服务

4.1 编写实体类

在order_service中创建cn.itcast.order.entity.Product(可从product_service中复制实体类Product)

4.2 编写web层

在order_service中创建cn.itcast.order.controller.OrderController

@RestController
@RequestMapping("/order")
public class OderController {
    @GetMapping("/buy/{id}")
    public Product buyById(@PathVariable Long id) {
        Product product = null;
        return product;
    }
}

5 服务调用

前文已经编写了二个基础的微服务,在用户下单时需要调用商品微服务获取商品数据。那应该怎么做呢?总人皆知商品微服务提供了供人调用的HTTP接口。所以可以再下定单的时候使用http请求的相关工具类完成,如常见的HttpClient,OkHttp,当然也可以使用Spring提供的RestTemplate

5.1 RestTemplate介绍

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

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

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

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

5.2 RestTemplate方法介绍

在这里插入图片描述

5.3 通过RestTemplate调用微服务

5.3.1 在 order_service工程中OrderApplication启动类 中配置RestTemplate

    //配置RestTemplate交给spring管理
    @Bean
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

5.3.2 编写下订单方法

    // 注入restTemplate
    @Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("/buy/{id}")
    public Product buyById(@PathVariable Long id) {
        Product product = null;
        product = restTemplate.getForObject("http://127.0.0.1:9001/product/1",Product.class);
        return product;
    }

5.4 硬编码存在的问题

至此已经可以通过RestTemplate调用商品微服务的RESTFul API接口。但是我们把提供者的网络地址(ip,端口)等硬编码到了代码中,这种做法存在许多问题:

  • 应用场景有局限
  • 无法动态调整
    那么应该怎么解决呢,就需要通过注册中心动态的对服务注册和服务发现
发布了33 篇原创文章 · 获赞 0 · 访问量 350

猜你喜欢

转载自blog.csdn.net/weixin_43596905/article/details/105108179