手动搭建微服务基本架构案例及步骤

创建一个maven项目

初始化父工程

  • 创建父工程, 工程名称, AarifactId, 包。
  • 父类工程中的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.itcast</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>
        <!-- spring boot 基本依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- spring boot 日志依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <!-- spring boot 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <!-- hutool快捷开发工具依赖,会有很多方便的util类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.4.1</version>
        </dependency>
        <!-- lombok注解依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
​
    <dependencyManagement>
        <dependencies>
            <!-- spring cloud依赖 -->
            <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></snapshots>-->
<!--        </repository>-->
<!--    </repositories>-->
</project>

product子模块创建

1. 创建产品服务的module

该模块名为product-service,提供product相关的一些接口,用于产品相关的业务逻辑,例如产品的增删改查等及产品周边的业务。子工程可以直接用父工程的大部分依赖。

2. 添加子工程依赖

因为子工程需要连接mysql数据库做查询,所以需要mysql的连接支持依赖和jpa数据库访问依赖

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

     父工程不需要src文件夹,因为父工程基本上都是依赖配置,不会有相应代码

product子模块创建

1. 创建产品服务的module

该模块名为product-service,提供product相关的一些接口,用于产品相关的业务逻辑,例如产品的增删改查等及产品周边的业务。子工程可以直接用父工程的大部分依赖。

2. 添加子工程依赖

因为子工程需要连接mysql数据库做查询,所以需要mysql的连接支持依赖和jpa数据库访问依赖

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

3. 创建子模块规范结构

主要包名为cn.itcast.product,实体类放在entity包下,持久层类放在dao包下,业务层放在service包下,API提供调用类放在controller包下。

              

4. 创建数据库及相关表

创建一个叫做shop的数据库,并且创建一张tb_product的产品表

create table tb_product (
    id int(11) NOT NULL AUTO_INCREMENT,
    product_name varchar(40) DEFAULT NULL COMMENT 'name',
    status int(2) DEFAULT NULL COMMENT 'status',
    price decimal(10,2) DEFAULT NULL COMMENT 'price per product',
    prodcut_desc varchar(255) DEFAULT NULL COMMENT 'description for product',
    caption varchar(255) DEFAULT NULL COMMENT 'title',
    inventory int(11) DEFAULT NULL COMMENT 'current count for product',
    primary key (id)
) engine=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

5. 创建实体类

entity包下面创建Product实体类,

  1. 需要在类上面加@Entity注解标明他是一个实体类。
  2. 需要在类上面加@Table注解连接他的实体表。
  3. 使用lombok包的@Data注解类,使其有get/set/toString等方法的自动生成。
  4. 主键标注为@ID,并且@GeneratedValue(strategy = GenerationType.IDENTITY) 主键由数据库自动生成(主要是自动增长型)
import java.math.BigDecimal;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Data;

/**
 * 商品实体类
 */
@Data
@Entity
@Table(name="tb_product")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String productName;
    private Integer status;
    private BigDecimal price;
    private String productDesc;
    private String caption;
    private Integer inventory;
}

6. 创建Dao数据持久接口

  1. 该dao层为interface
  2. 继承了JpaRepository 用于数据库访问,范型键为产品类Product,值为产品类主键类型Long
  3. 还要继承JpaSpecificationExecutor,范型为产品类Product

两者均提供了一些基本的查询方法,如findAll(), findAll(Sort), save(Iterable), delete(Id)

import cn.itcast.product.entity.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
​
/**
 * 商品持久层DAO
 * 继承
 */
public interface ProductDao extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {
}

7. 创建serivce层接口和实现类

  • 接口service

import cn.itcast.product.entity.Product;
​
public interface ProductService {
    Product findById(Long Id);
    void save(Product product);
    void update(Product product);
    void delete(Long Id);
}
  • 实现类
  1. 使用@Serivce注解类表明是一个service,并且交给容器管理。
  2. 实现Serivce接口并实现其所有方法
  3. 注入Dao接口,因为Serivce类中需要使用dao操作数据库
import cn.itcast.product.dao.ProductDao;
import cn.itcast.product.entity.Product;
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(final Long Id) {
        return productDao.findById(Id).get();
    }

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

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

    @Override public void delete(final Long Id) {
        productDao.deleteById(Id);
    }
}

8. 创建Controler层和类

  1. 需要使用@RestController注解类,表示该类为一个controller类,并交给容器管理
  2. @RequestMapping注解标注,表示基本访问路径是什么
  3. 注入ProductService接口,因为需要使用service做业务逻辑。
import cn.itcast.product.entity.Product;
import cn.itcast.product.serivce.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

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

    @Autowired
    private ProductService productService;

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public Product findById(@PathVariable Long id) {
        return productService.findById(id);
    }

    @RequestMapping(value = "", method = RequestMethod.POST)
    public String save(@RequestBody Product product) {
        productService.save(product);
        return "save successfully";
    }
}

9. 创建springboot启动类

在product包最外层创建启动类。

  1. 注解@SpringBootApplication表示该类是spring boot的Application启动类
  2. 注解@EntityScan代表我要扫描的实体类注解
  3. 配置启动main方法。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;

@SpringBootApplication
@EntityScan("cn.itcast.product.entity")
public class ProductApplication {

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

10. 创建配置文件

在resources包下面我们需要增加一个application.yml配置文件,用于项目启动。

  1. server.port=8081
  2. application.name=service-product
  3. datasource 数据源配置见下图
  4. jpa配置数据库类型,是否show sql是否open-in-view
server:
    port: 8081
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: root
    jpa:
        database: MySQL
        show-sql: true
        open-in-view: true

11. 启动测试

启动spring boot的启动类,解决启动时候出现的问题,如果启动成功,测试controller的方法。

Order子模块创建

按照product子模块的创建方式,创建一个Order子模块

  1. 创建产品服务的module
  2. 添加子工程依赖
  3. 创建子模块规范结构
  4. 创建数据实体表
  5. 创建实体类
  6. 创建Dao数据持久接口
  7. 创建serivce层接口和实现类
  8. 创建Controler层和类
  9. 创建springboot启动类
  10. 创建配置文件

注意: 因为order服务也需要使用到product实体对象,所以我们需要拷贝一份实体对象,但是不需要设置数据库连接等注解, 例如@Entity和@Table。

 

模块间通讯

因为根据业务需要,我们要订单服务模块访问到产品服务模块,这就涉及到了两个模块之间的通讯方式。如何让订单服务模块调用到商品服务模块? 我们是通过在订单服务模块中直接调用商品服务模块的rest api

以下都是模块间通讯的方式:

  1. Java中自带的HttpURLConenction
  2. Apache HttpComponents
  3. OKHttp
  4. 其他url调用工具、
  5. Spring提供的RestTemplate
  • RestTemplate介绍

介绍: 他是Spring框架提供的一套可用于在应用中调用rest服务的类,他简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接,我们只需要传入url及返回值类型就可以了。

RestTemplate默认依赖JDK提供的http连接能力(HttpURLConenction),如果又需要的话 也能通过setRequestFactory方法替换为Apache HttpComponents,Netty或者OKHttp等其他的HTTP library。

该模版类的主要切入点为以下几个方法(并对应着HTTP的六个主要方法):

  • RestTemplate的使用

在order服务的入口类中创建RestTemplate对象

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.itcast.order.entity")
public class OrderApplication {

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

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

    在OrderContoller使用的时候调用对应的方法完成操作

import cn.itcast.order.entity.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 {

    @Autowired
    public RestTemplate restTemplate;

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

测试跨模块调用

启动两个模块, 访问 http://localhost:8082/order/buy/3 获得下面输入表示模块间通讯正常

{"id":3,"productName":"book","status":1,"price":12.30,"productDesc":"the good book in my local database","caption":"methodway to go","inventory":300}

手动搭建所包含的问题

  1. 调用地址硬编码到了微服务的java代码中。
  2. 正式应用的时候微服务会有一大堆,调用方端需要记录维护每一个其他微服务的地址。增加了开发难度
  3. 微服务调用做负载均衡会有问题。
  4. 调用关系链路复杂,导致追踪困难

解决办法:
     1. 加入API网关

     2. 统一整个系统的配置统一管理

     3. 链路追踪

 

 

附录

lombok注解说明

注解名称功能
@Setter自动添加类中所有属性相关的 set 方法
@Getter自动添加类中所有属性相关的 get 方法
@Builder使得该类可以通过 builder (建造者模式)构建对象
@RequiredArgsConstructor生成一个该类的构造方法,禁止无参构造
@ToString重写该类的toString()方法
@EqualsAndHashCode重写该类的equals()hashCode()方法
@Data等价于上面的@Setter@Getter@RequiredArgsConstructor@ToString
@EqualsAndHashCode

JpaRepository说明

JpaRepository 中有很多基础查询方法,他还继承了PagingAndSortingRepository类实现了分页操作。

而PagingAndSortingRepository还继承于CrudRepository, 这里面也提供了大量的数据库基本查询方法。

如count(), deleteById(ID var1), delete(T var1), deleteAll(Iterable<? extends T> var1), deleteAll()

findAll(); findById(ID var1); existsById(ID var1), findAllById(Iterable var1),

save(S var1)

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
    <S extends T> S save(S var1);
​
    <S extends T> Iterable<S> saveAll(Iterable<S> var1);
​
    Optional<T> findById(ID var1);
​
    boolean existsById(ID var1);
​
    Iterable<T> findAll();
​
    Iterable<T> findAllById(Iterable<ID> var1);
​
    long count();
​
    void deleteById(ID var1);
​
    void delete(T var1);
​
    void deleteAll(Iterable<? extends T> var1);
​
    void deleteAll();
}

JPA提供的四种GenerationType

  • TABLE:使用一个特定的数据库表格来保存主键。
  • SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。
  • IDENTITY:主键由数据库自动生成(主要是自动增长型)
  • AUTO:主键由程序控制。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值