电商系统微服务架构一览

328 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情

一、微服务发展

1.1 微服务架构

1.1.1 springcloud全家桶

一般是eureka为注册中心,feign+ribbon作为RPC调用,zuul或gateway作为网关,hystrix作为服务降级,用zipkin和sleuth作为链路监控,config作为配置中心,stream做消息中间件集成,还有springSecurity配合做安全认证.

1.1.2 dubbo

当然还有部分中小公司使用dubbo+zookeeper.

1.1.3 大厂自研组件

很多大厂都是自研组件,部分大厂以dubbo为核心自研组件.

1.1.4 springcloud alibaba

阿里把自己的微服务技术融入进springcloud体系和标准,nacos、dubbo、seata(分布式事务)、sentinal(限流降级)、rocketmq. 另外再加上携程开源的配置中心apollo、大众点评开源的链路监控(CAT)

因为他维护活跃、又是中文文档、中文社区.未来可能是国内微服务框架的主流

二、注册中心选型

2.1 注册中心对比

2.1.1 eureka

AP模型,设计之初就是想保证可用性.各个server之间是相互独立的,不存在leader.只要有一个leader存活就还能提供注册.

2.1.2 zookeeper

典型的CP,牺牲可用性保障数据一致性.当他的master挂了之后会停止服务,直到选出新的leader.

2.1.3 consul

基于raft算法的CP模型

2.1.4 nacos

基于raft算法的CP模型,也可以支持配置成AP,功能最强大,包括雪崩保护、自动注销实例、监听支持、多数据中心.

2.2 nacos部署

详细官方文档:nacos.io/zh-cn/docs/…

在0.7版本之前,在单机模式时nacos使用嵌入式数据库实现数据的存储,不方便观察数据存储的基本情况。0.7版本增加了支持mysql数据源能力,具体的操作步骤:

  • 安装数据库,版本要求:5.6.5+
  • 初始化mysql数据库,数据库初始化文件:nacos-mysql.sql
  • 修改conf/application.properties文件,增加支持mysql数据源配置(目前只支持mysql),添加mysql数据源的url、用户名和密码。
spring.datasource.platform=mysql

db.num=1
db.url.0=jdbc:mysql://11.162.196.16:3306/nacos_devtest?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=nacos_devtest
db.password=youdontknow
2.2.1 单机版部署
  1. 只需要解压开之后,到bin目录下执行:

sh startup.sh -m standalone

  1. 访问http://localhost:8848/nacos

账户:nacos 密码:nacos

常见问题:

访问http://localhost:8848/nacos/v1/ns/service/list 提示server is DOWN now, please try again later!

删除掉nacos/data目录重新启动

2.2.2 多环境隔离
  1. 在nacos上创建一个命名空间,如dev,id也填dev

  2. 在properties配置里增加这行:

spring.cloud.nacos.discovery.namespace=dev

三、分布式事务选型

除了阿里的Seata之外,目前还没有特别成熟的框架.像ByteTcc、LCN虽然还不错,但是毕竟只是少数人用,如果要拿到生产上去使用,必须要确保自己能吃透里面的源码,以保证出了bug自己能够修改.

Seata支持的分布式方案:Tcc、XA、AT、Saga

AT:是通过记录sql执行的日志,通过做反向日志来进行回滚的方案,接口层面就不用关心数据回滚了.

Saga: 长事务,多个事务串联在一起

3.1 安装Seata

下载地址: github.com/seata/seata…

修改启动脚本,主要是改小配置,电脑内存够也可以不改.

启动命令:

sh seata-server.sh -p 8091 -h 127.0.0.1 -m file

3.2 Seata+Nacos+SpringCloudAlibaba部署指南

我使用的版本:

SpringCloudAlibaba : 2.1.1.RELEASE

Nacos : 1.1.4

Seata : 0.9

版本非常重要,有时版本不对,项目启动会报一些莫名其妙的错误.

3.2.1 安装Nacos

下载地址:github.com/alibaba/nac…

解压之后,如果是单机部署的话,可以点击修改startup.sh中的MODE为standalone.


export SERVER="nacos-server"
# 修改MODE
export MODE="standalone"
export FUNCTION_MODE="all"

==注意如果电脑ip变化了,需要删除/nacos/data文件夹,然后在重启.否则会提示服务down==

启动命令

startup.sh

3.2.2 安装seata
  1. 下载地址:github.com/seata/seata…
  2. 解压seata文件夹
  3. 修改seata/conf/file.conf文件

改动点:

  • 修改自定义事务名称,server 中 vgroup_mapping.my_test_tx_group 的名称,随意定义
  • 修改事务日志存储模式为db。(默认为文件)store中的mode
  • 修改数据库信息
transport {
  # tcp udt unix-domain-socket
  type = "TCP"
  #NIO NATIVE
  server = "NIO"
  #enable heartbeat
  heartbeat = true
  #thread factory for netty
  thread-factory {
    boss-thread-prefix = "NettyBoss"
    worker-thread-prefix = "NettyServerNIOWorker"
    server-executor-thread-prefix = "NettyServerBizHandler"
    share-boss-worker = false
    client-selector-thread-prefix = "NettyClientSelector"
    client-selector-thread-size = 1
    client-worker-thread-prefix = "NettyClientWorkerThread"
    # netty boss thread size,will not be used for UDT
    boss-thread-size = 1
    #auto default pin or 8
    worker-thread-size = 8
  }
  shutdown {
    # when destroy server, wait seconds
    wait = 3
  }
  serialization = "seata"
  compressor = "none"
}
service {
  #vgroup->rgroup
  # 1.自定义事务组名称
  vgroup_mapping.my_test_tx_group = "xiangjiao_tx_group"
  #only support single node
  default.grouplist = "127.0.0.1:8091"
  #degrade current not support
  enableDegrade = false
  #disable
  disable = false
  #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  max.commit.retry.timeout = "-1"
  max.rollback.retry.timeout = "-1"
}

client {
  async.commit.buffer.limit = 10000
  lock {
    retry.internal = 10
    retry.times = 30
  }
  report.retry.count = 5
  tm.commit.retry.count = 1
  tm.rollback.retry.count = 1
}

## transaction log store
store {
  ## store mode: file、db
  # 2.修改为db
  mode = "db"

  ## file store
  file {
    dir = "sessionStore"

    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    max-branch-session-size = 16384
    # globe session size , if exceeded throws exceptions
    max-global-session-size = 512
    # file buffer size , if exceeded allocate new buffer
    file-write-buffer-cache-size = 16384
    # when recover batch read size
    session.reload.read_size = 100
    # async, sync
    flush-disk-mode = async
  }

  ## database store
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc.
    datasource = "dbcp"
    ## mysql/oracle/h2/oceanbase etc.
    db-type = "mysql"
    driver-class-name = "com.mysql.jdbc.Driver"
    url = "jdbc:mysql://127.0.0.1:3306/seata"
    # 3.修改数据库信息
    user = "root"
    password = "root"
    min-conn = 1
    max-conn = 3
    global.table = "global_table"
    branch.table = "branch_table"
    lock-table = "lock_table"
    query-limit = 100
  }
}
lock {
  ## the lock store mode: local、remote
  mode = "remote"

  local {
    ## store locks in user's database
  }

  remote {
    ## store locks in the seata's server
  }
}
recovery {
  #schedule committing retry period in milliseconds
  committing-retry-period = 1000
  #schedule asyn committing retry period in milliseconds
  asyn-committing-retry-period = 1000
  #schedule rollbacking retry period in milliseconds
  rollbacking-retry-period = 1000
  #schedule timeout retry period in milliseconds
  timeout-retry-period = 1000
}

transaction {
  undo.data.validation = true
  undo.log.serialization = "jackson"
  undo.log.save.days = 7
  #schedule delete expired undo_log in milliseconds
  undo.log.delete.period = 86400000
  undo.log.table = "undo_log"
}

## metrics settings
metrics {
  enabled = false
  registry-type = "compact"
  # multi exporters use comma divided
  exporter-list = "prometheus"
  exporter-prometheus-port = 9898
}

support {
  ## spring
  spring {
    # auto proxy the DataSource bean
    datasource.autoproxy = false
  }
}
  1. 创建数据库seata

新建一个数据库seata,并执行conf/db_store.sql文件,建立所需的表

另外还需要在你的每个业务数据库中执行conf/db_undo_log.sql 5. 修改conf/registry.conf配置文件

修改type为nacos

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    serverAddr = "localhost:8848"
    namespace = ""
    cluster = "default"
    username="nacos"
    password="nacos"
  }
  1. 启动seata

seata-server.sh

启动完成后,可以登陆nacos,127.0.0.1/8848/nacos看看服务是否注册成功

==我出现过登陆时用户名密码不对的情况,后来重新配置了JDK环境变量解决了.==

  1. 接下来就是项目了,项目方面就不多描述了,直接看我的项目怎么配置就行了.

除了使用全局事务注解@GlobalTransactional(name = "createReport",rollbackFor = Exception.class)外,还需要配置数据源:

package com.mmc.reward.config;

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;
import java.io.IOException;

/**
 * Description:
 * author: yu.hb
 * Date: 2019-11-01
 */
@Configuration
@EnableConfigurationProperties({MybatisProperties.class})
public class DataSourceConfiguration {


    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return new DruidDataSource();
    }

    @Bean
    public DataSourceProxy dataSourceProxy(DataSource dataSource) {
        return new DataSourceProxy(dataSource);
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSourceProxy dataSourceProxy,
                                                       MybatisProperties mybatisProperties) {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSourceProxy);

        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        try {
            Resource[] mapperLocaltions = resolver.getResources(mybatisProperties.getMapperLocations()[0]);
            bean.setMapperLocations(mapperLocaltions);

            if (StringUtils.isNotBlank(mybatisProperties.getConfigLocation())) {
                Resource[] resources = resolver.getResources(mybatisProperties.getConfigLocation());
                bean.setConfigLocation(resources[0]);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return bean;
    }
}

==注意: 分布式事务分组名是对应的seata/conf/file.conf中的,3个地方都是配一样的,我这里配的是xiangjiao_tx_group==

vgroup_mapping.my_test_tx_group = "xiangjiao_tx_group"

对应项目中application.properties中的


spring.cloud.alibaba.seata.tx-service-group=xiangjiao_tx_group

对应项目中的file.conf文件中的

  vgroup_mapping.xiangjiao_tx_group = "default"

四、熔断限流选型

Sentinel和Hystix对比

Sentinel的功能有流量控制、熔断降级、系统负载保护、监控等.

在熔断降级的设计上,Sentinel和Hystrix采取了完全不一样的方式.

Hystrix:

  • 采用的上线程池隔离的方式,来对依赖进行了隔离.这样做的好处是资源与资源之间进行了彻底的隔离.缺点是增加了切换线程的成本.并且需要给资源提前分配线程池大小.

Sentinel:

  • 通过线程并发数控制

线程数如果被堆积了,到一定数量,新请求就会被拒绝

  • 通过响应时间对资源进行降级

当依赖的资源出现响应时间超时时,所有对该资源对访问都会被直接拒绝.

熔断策略有三种:

  • 慢调用比例
  • 异常比例
  • 异常数

五、配置中心选型

常用的配置中心: SpringCloudConfig、Nacos、Apollo

Apollo比较重,适用于中大型公司.小公司来说Nacos就够用了,或者如果是使用的SpringCloud这一套,那么就使用SpringCloudConfig.

六、监控中心选型

2015-2016用的比较火的是Zabbix,2018-2019用的比较火的是Prometheus

Prometheus可以监控内存、cpu、中间件如Redis,并且可以进行报警.

监控机器资源、JVM进程、系统(QPS、延时、异常)

业务指标:异常、QPS、接口时延

七、接入日志中心

如果节点机器数多了,想找日志就非常麻烦,每台机器去找日志非常费时间.

知名一点的开源的日志中心较少,一般公司会自研日志中心.一般采用Es+Hbase来实现.

八、链路追踪选型

Sleuth、Zipkin、Cat、Skywalking

CAT:美团点评开源,CAT很大的优势是实时

九、API网关选型对比

Zuul、Nginx+Lua、Kong