什么是微服务架构

发布于:2022-12-10 ⋅ 阅读:(589) ⋅ 点赞:(0)

目录

概念:

面向服务架构(SOA):

微服务

由来:

定义:

1、单一职责的

2、面向服务的

3、独立数据库的

微服务间的通信方式

微服务项目部署:

生产环境:

发版本:

微服务优点

微服务缺点

微服务实例:

文件结构

建立两个数据库:

Service_user模块

service_ticket模块


 

概念:

微服务(或微服务架构)系统是一个分布式系统,按照业务域划分为独立的服务单元,是一种云原生架构方法,其中单个应用程序由许多松散耦合且可独立部署的较小组件或服务组成。

 

面向服务架构(SOA):

SOA架构是一个面向服务的组件模型,他将原来的单体架构按照不同功能单元进行拆分为不同的子系统,通过在这些服务之间定义定义良好接口联系起来,一个模块为一个子系统,一个项目有主库和从库,主从库数据同步,以下图例将每个服务独立起来,对外提供功能。

2df228185a914c2eb431a90825f950df.png

 

 

微服务

由来:

        当用户访问量变大,单体架构应用服务器无法支撑时,加服务器,加负载均衡,分离静态文件(前后端分离),都解决不了应用缺点,因此在面向服务的体系架构方法论上演变出面向服务的架构思想,———  微服务

定义:

1、单一职责的

一个微服务解决一个业务问题,每个功能单元是一个可独立替换,独立升级的软件单元。

2、面向服务的

将自己的业务能力进行封装并对外提供服务,一个应用是一小组小型服务,可通过http方式进行互通。

3、独立数据库的

随着业务扩张,服务与服务之间不需要提供数据库集成,而是提供API接口相互调用。

 

微服务间的通信方式

既然微服务是将每个业务域开发成一个微服务运行,那么微服务之间需要通信方式,采用轻量级机制通信,通常为HTTP、Restful、API。每个服务只需要对外提供一些HTTP请求的资源(接口)即可。

 

 

微服务项目部署:

a5c1609310a742ebadeafc61d4173f2b.png

 

生产环境:

就是项目准备上线时,放到网页或app上,模仿用户测试所产生了真实的数据,也是让用户正常使用的环境,生成环境一般是在用户使用量少的时间段下进行发布,这样生成环境就算出现错误,也能把损失降到最低。

发版本:

指的是程序在某个时刻需要更新新功能或维护,可能需要停服更新,或不停服更新,此时的微服务项目有40个模块,则有40个jar需要部署。

为了方便模块部署,一般将项目放到git仓库中,使用CI/CD持续集成持续交付的方式,直接将提前构建好的环境更新项目。

 

微服务优点

  1. 逻辑清晰,因为是业务与业务间独立。
  2. 简化部署
  3. 可扩展,分布式系统中通常要采用Scale out的方式进行扩展。因为不同的功能会面对不同的负荷变化,因此采用微服务的系统相对单块系统具备更好的可扩展性。
  4. 灵活组合:各个模块之间可以重用,灵活性高
  5. 技术异构,因为松耦合性,每个微服务之间可以使用不同编程语言或者技术,只要实现功能,什么技术都不限。
  6. 高可靠,微服务间独立部署,一个微服务的异常不会导致其它微服务同时异常。通过隔离、融断等技术可以避免极大的提升微服务的可靠性。

 

微服务缺点

1、复杂度高

微服务间通过REST、RPC等形式交互,相对于Monolithic模式下的API形式,需要考虑被调用方故障、过载、消息丢失等各种异常情况,代码逻辑更加复杂。

对于微服务间的事务性操作,因为不同的微服务采用了不同的数据库,将无法利用数据库本身的事务机制保证一致性,需要引入二阶段提交等技术。

同时,在微服务间存在少部分共用功能但又无法提取成微服务时,各个微服务对于这部分功能通常需要重复开发,或至少要做代码复制,以避免微服务间的耦合,增加了开发成本。

 

2、运维复杂

3、影响性能

微服务间通过REST、RPC等形式交互,通信时延会有影响。

另外:在开发新系统时,因为业务逻辑和系统边界并不清晰,可以先采用Monolithic方式进行开发,直到能够识别出稳定逻辑后再进行微服务拆分,从而避免边界不清而进行重划分所带来的返工。

微服务实例:

文件结构

b42c5585b58a40e3878b7df84f49928b.png

09bc18c0e9f243ae932b63ef0e022c22.png

3b4387858b184411a4b5fb595cc722d2.png

建立两个数据库:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `name` varchar(255) DEFAULT NULL COMMENT '用户姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `sex` varchar(255) DEFAULT NULL COMMENT '性别',
  `addr` varchar(255) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4
//随便插入一条数据
CREATE TABLE `ticket` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '门票id',
  `ticket_price` float DEFAULT NULL COMMENT '门票价格',
  `uid` int(11) DEFAULT NULL COMMENT '用户id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4
//随便插入一条数据

Service_user模块

 1、创建一个module名为service_user,并创建实体类、UserDao.java接口和UserDao.xml

package service_user.mojingyi.Entity;
import lombok.Data;
import java.io.Serializable;

@Data
public class User implements Serializable {
    /* 用户id*/
    private Integer id;

    /* 用户姓名*/
    private String name;

    /* 年龄*/
    private Integer age;

    /* 性别*/
    private String sex;

    /* 地址*/
    private String addr;

    private static final long serialVersionUID = 1L;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="service_user.mojingyi.Dao.UserDao">
  <resultMap id="BaseResultMap" type="service_user.mojingyi.Entity.User">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="age" jdbcType="INTEGER" property="age" />
    <result column="sex" jdbcType="VARCHAR" property="sex" />
    <result column="addr" jdbcType="VARCHAR" property="addr" />
  </resultMap>
  <sql id="Base_Column_List">
    id, `name`, age, sex, addr
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from user
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from user
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" keyColumn="id" keyProperty="id" parameterType="service_user.mojingyi.Entity.User" useGeneratedKeys="true">
    insert into user (`name`, age, sex, 
      addr)
    values (#{name,jdbcType=VARCHAR}, #{age,jdbcType=INTEGER}, #{sex,jdbcType=VARCHAR}, 
      #{addr,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" keyColumn="id" keyProperty="id" parameterType="service_user.mojingyi.Entity.User" useGeneratedKeys="true">
    insert into user
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="name != null">
        `name`,
      </if>
      <if test="age != null">
        age,
      </if>
      <if test="sex != null">
        sex,
      </if>
      <if test="addr != null">
        addr,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="age != null">
        #{age,jdbcType=INTEGER},
      </if>
      <if test="sex != null">
        #{sex,jdbcType=VARCHAR},
      </if>
      <if test="addr != null">
        #{addr,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="service_user.mojingyi.Entity.User">
    update user
    <set>
      <if test="name != null">
        `name` = #{name,jdbcType=VARCHAR},
      </if>
      <if test="age != null">
        age = #{age,jdbcType=INTEGER},
      </if>
      <if test="sex != null">
        sex = #{sex,jdbcType=VARCHAR},
      </if>
      <if test="addr != null">
        addr = #{addr,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="service_user.mojingyi.Entity.User">
    update user
    set `name` = #{name,jdbcType=VARCHAR},
      age = #{age,jdbcType=INTEGER},
      sex = #{sex,jdbcType=VARCHAR},
      addr = #{addr,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>
package service_user.mojingyi.Dao;

import service_user.mojingyi.Entity.User;


public interface UserDao {
    int deleteByPrimaryKey(Integer id);

    int insert(User record);

    int insertSelective(User record);

    User selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(User record);

    int updateByPrimaryKey(User record);
}

 2、创建与前端交互的Controller、Controller要调用的service接口、serviceImpl实现类

package service_user.mojingyi.Controller;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import service_user.mojingyi.Entity.User;
import service_user.mojingyi.Service.UserService;
import javax.annotation.Resource;
//该Controller使用restful方式传参,调用查询方法,使前端输出数据
@RestController
@RequestMapping("/user")
public class useController {

    @Resource
    UserService userService;
    @RequestMapping("/{id}")
    public User querySelectId(@PathVariable("id") Integer id){
        System.out.println(userService.queryUser(id));
        return  userService.queryUser(id);

    }
}
//接口
public interface UserService {
    User queryUser(int id);
}

//接口实现,调用UserDao的查询方法查询数据。
@Service("UserService")//不要忘记这个注释
public class UserServiceImpl implements UserService {
    @Resource
    UserDao userDao;
    @Override
    public User queryUser(int id) {
        User user = userDao.selectByPrimaryKey(id);

        return user;
    }
}

3、配置文件application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver #数据库驱动程序,如果是mysql5.X版本,没有cj。mysql8.0是com.mysql.cj.jdbc.Driver
    #mysql8.0的url需要加上时区配置,否则会连接不上
    url: jdbc:mysql://localhost:3307/user?useUnicode=ture&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    username: root
    password: 123456

mybatis:
  configuration:
    map-underscore-to-camel-case: true
  mapper-locations: classpath:mapper/**/*.xml #该路径必须是UserDao.xml的所存路径

4、启动文件

@SpringBootApplication
@MapperScan("service_user.mojingyi.Dao")
public class ServiceUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceUserApplication.class, args);
    }
}

service_ticket模块

该模块需要使用到service_user模块的查询数据功能,并与ticket数据库的数据一块输出

实现方式是,在 Ticket 实体中添加一个对象属性 private User user;并使用  restTemplate  获取user实例。将该 user 实例注入到 ticket 中。(该过程主要在impl中实现)

TicketController

@Controller
@RequestMapping("/ticket")
public class TicketController {
    @Resource
    TicketService ticketService;
    @GetMapping("/{id}")
    public Ticket selectTicketByID(@PathVariable("id") Integer id){
        return ticketService.findQuerybyId(id);
    }
}

实体类:Ticket

package service_ticket.mojingyi.Entity;
import lombok.Data;
import org.apache.tomcat.jni.User;
import java.io.Serializable;

@Data
public class Ticket implements Serializable {

    private User user;
    /* 门票id*/
    private Integer id;
    /* 门票价格*/
    private Double ticketPrice;
    /* 用户id*/
    private Integer uid;

    private static final long serialVersionUID = 1L;
}

TicketDao

public interface TicketDao {

    Ticket selectByPrimaryKey(Integer id);

}

TicketMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="service_ticket.mojingyi.Dao.TicketDao">
  <resultMap id="BaseResultMap" type="service_ticket.mojingyi.Entity.Ticket">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="ticket_price" jdbcType="FLOAT" property="ticketPrice" />
    <result column="uid" jdbcType="INTEGER" property="uid" />
  </resultMap>
  <sql id="Base_Column_List">
    id, ticket_price, `uid`
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from ticket
    where id = #{id,jdbcType=INTEGER}
  </select>
  
</mapper>

TicketService

public interface TicketService {
        Ticket findQuerybyId(Integer id);
}

TicketServiceImpl

@Service("TicketService")
public class TicketServiceImpl implements TicketService {

    @Resource
    TicketDao ticketDao;
    @Resource
    RestTemplate restTemplate;

    @Override
    public Ticket findQuerybyId(Integer id) {
        Ticket ticket = ticketDao.selectByPrimaryKey(id);
        /*修改原来service_ticket中TicketServiceImpl类,,,将查询到的User信息注入到Ticket中*/
        String Url = "http//localhost:8080/user/"+ticket.getUid();
        User user = restTemplate.getForObject(Url,User.class);
        ticket.setUser((org.apache.tomcat.jni.User) user);

        return ticket;
    }
}

启动类:

@SpringBootApplication
@MapperScan("service_ticket.mojingyi.Dao")
public class ServiceTicketApplication {

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

    /*微服务例子第一步,引入该类,再去implement中用此方法*/
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

配置类:application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver #数据库驱动程序,如果是mysql5.X版本,没有cj。mysql8.0是com.mysql.cj.jdbc.Driver
    #mysql8.0的url需要加上时区配置,否则会连接不上
    url: jdbc:mysql://localhost:3307/service_ticket?useUnicode=ture&characterEncoding=UTF-8&serverTimezone=GMT%2B8
    username: root
    password: 123456
mybatis:
  configuration:
    map-underscore-to-camel-case: true
  mapper-locations: classpath:mapper/mybatis/**/*.xml

以上的例子就是模拟微服务的实现过程,真正的微服务项目可以用SpringCloud框架和一些组件去实现模块与模块之间的交互。6b2b30b9d1e64b5483800acb44b6f579.png

 

 

本文含有隐藏内容,请 开通VIP 后查看