SpringCloud学习总结(三)——案例环境搭建

案例准备

用例spring_cloud_demo 项目地址:传送门

 

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

服务提供者:服务的被调用方,提供调用接口的一方

服务消费者:服务的调用方,依赖于其他服务的一方

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

 

 

 

注意:后续学习的过程中都以下列项目例子进行学习;

 

一、数据库表

用户表

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

 

 

商品表

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

 

 

订单表

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

 

 

二、 项目搭建环境

1、创建父工程spring_cloud_demo

在IDEA中创建父工程spring_cloud_demo 并引入坐标,pom文件如下:

<
?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><groupId>cn.hzp</groupId>
    <artifactId>spring_cloud_demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>product_service</module>
    </modules>
​
​
    <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>
        <!--省略代码中 类的get set的方法-->
        <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></project>

 

2、创建微服务工程子模块

  • 创建订单微服务模块 order_service

 

 

 

  • 创建商品微服务模块 product_service

 

 

 

 

 

子项目依赖坐标:

 <dependencies>
        <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></dependencies>

 

 

编写实体类

product_service模块

package cn.hzp.product.domain;
​
import lombok.Data;
​
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.math.BigDecimal;
​
/**
 * 商品实体类-product_service模块
 */
@Data
@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;
    private Integer inventory;
}
​

 

order_service模块

package cn.hzp.order.domain;
​
import lombok.Data;
​
import java.math.BigDecimal;
​
/**
 * 商品实体类-order_service模块
 */
@Data
public class Product {
    private Long id;
    private String productName;
    private Integer status;
    private BigDecimal price;
    private String productDesc;
    private String caption;
    private Integer inventory;
}
​

 

这里使用了 lombok简化实体类的开发

Lombok能以简单的注解形式来简化java代码,提高开发人员的开发效率

 

编写dao接口

在product_service模块中创建 ProductDao 接口

package cn.hzp.product.dao;
​
import cn.hzp.product.domain.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
​
/**
 * 接口继承
 * 使用spring的jpa功能生成增删改查的接口
 */
public interface ProductDao extends JpaRepository<Product,Long>, JpaSpecificationExecutor<Product> {
​
}
​

 

编写service层

在product_service模块中创建 ProductService

package cn.hzp.product.service;
​
import cn.hzp.product.domain.Product;
​
public interface ProductService {
    /**
     * 根据id查询
     */
    Product findById(Long id);
​
    /**
     * 保存
     */
    void save(Product product);
    /**
     * 更新
     */
    void update(Product product);
    /**
     * 删除
     */
    void delete(Long id);
}
​

 

在product_service模块中创建ProductServiceImpl 实现类

package cn.hzp.product.service.cn.hzp.product.service;
​
import cn.hzp.product.dao.ProductDao;
import cn.hzp.product.domain.Product;
import cn.hzp.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
​
@Service
public class ProductServiceImpl implements ProductService {
​
    @Autowired
    private ProductDao productDao;
​
    @Override
    public Product findById(Long id) {
        return productDao.findById(id).get();
    }
​
    @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);
    }
}
​

 

编写web层

在product_service模块中创建ProductController

package cn.hzp.product.controller;
​
import cn.hzp.product.domain.Product;
import cn.hzp.product.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
​
@RestController
@RequestMapping("/product")
public class ProductController {
​
    @Autowired
    private ProductService productService;
​
​
    @RequestMapping(value = "/{id}",method = RequestMethod.GET)
    public Product findById(@PathVariable Long id) {
        Product product = productService.findById(id);
        return product;
    }
​
    @RequestMapping(value = "",method = RequestMethod.POST)
    public String save(@RequestBody Product product) {
        productService.save(product);
        return "保存成功";
    }
}

 


在order_service模块中创建OrderController

package cn.hzp.order.controller;;
​
import cn.hzp.order.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
​
@RestController
@RequestMapping("/order")
public class OrderController {//注入restTemplate对象
    @Autowired
    private RestTemplate restTemplate;
​
    @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
    public Product findById(@PathVariable Long id) {
        Product product = restTemplate.getForObject("http://localhost:9001/product/"+id,Product.class);
        return product;
    }
​
}
 

controller中使用的@GetMapping是一个组合注解,相当与@RequestMapping(method="get")。

类似的注解还有@PostMapping,@PutMapping,@DeleteMapping

 

配置启动类

在product_service模块中创建 ProductApplication 启动类

package cn.hzp.product;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
​
@SpringBootApplication
@EntityScan("cn.hzp.product.domain")
public class ProductApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class,args);
    }
}

 


在order_service模块中创建OrderApplication 启动类

package cn.hzp.order;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
​
@SpringBootApplication
@EntityScan("cn.hzp.order.domain")
public class OrderApplication {
​
    /**
     * 使用spring提供的RestTemplate发送http请求到商品服务
     *      1.创建RestTemplate对象交给容器管理
     *      2.在使用的时候,调用其方法完成操作 (getXX,postxxx)
     */
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
​
    public static void main(String[] args) {
        SpringApplication.run(OrderApplication.class,args);
    }
}
​

 

 

配置yml文件

 

在product_service模块中创建 application.yml配置文件

server:
  port: 9001 #端口
spring:
  application:
    name: service-product #服务名称
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.126.99:3306/spring_cloud_demo?useUnicode=true&characterEncoding=utf8
    username: root
    password: root
  jpa:
    database: MySQL
    show-sql: true
    open-in-view: true

 


在order_service模块中创建 application.yml配置文件

server:
  port: 9002 #端口
spring:
  cloud:
    loadbalancer:
      retry:
        enabled: true # 开启Spring Cloud的重试功能
  application:
    name: service-order #服务名称
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.126.99:3306/spring_cloud_demo?useUnicode=true&characterEncoding=utf8
    username: root
    password: root
  jpa:
    database: MySQL
    show-sql: true
    open-in-view: true

 


 

三、服务调用RestTemplate

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

RestTemplate介绍

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

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()等方法。

RestTemplate方法

 

 

通过RestTemplate调用微服务

项目例子中通过订单order_service服务去调用商品product_service服务

@RestController
@RequestMapping("/order")
public class OrderController {//注入restTemplate对象
    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/buy/{id}",method = RequestMethod.GET)
    public Product findById(@PathVariable Long id) {
        Product product = restTemplate.getForObject("http://localhost:9001/product/"+id,Product.class);
        return product;
    }

}

 

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

  • 应用场景有局限

  • 无法动态调整url

那么应该怎么解决呢,就需要通过注册中心动态的对服务注册和服务发现

 


感谢itheima提供的材料

posted @ 2020-03-12 22:08  hzzz1024  阅读(577)  评论(0编辑  收藏  举报