Dinosaur Store
恐龙商城是一个B2C模式的电商平台,销售自营商品给客户。
介绍
1 微服务
把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程,并使用轻量级通信机制(通常是HTTP API)。
2 集群&分布式
集群是个物理形态,分布式(多个业务运行在不同的机器,所有业务构成一个大型的业务)是个工作方式。
3 远程调用(RPC)
分布式系统中,各个服务可能处于不同的主机,但是服务之间不可避免相互调用。 (?Java PRC的dubbo是不是也是呢?注册中心)
SpringCloud中使用HTTP+JSON的方式完成远程调用,比如订单服务给商品模块发送HTTP请求。
4 负载均衡
不能让每一台服务器太忙或者太闲。
轮询
最小连接
散列
5 服务注册/发现&注册中心
A调用B,但是A不知道B服务当前有多少台,那些正常,所以引入注册中心。
dubbo这个Java RPC框架使用zookeeper注册中心。
6 配置中心
每个服务可能部署在多机器,让每个服务从配置中心中获取自己的配置。
7 服务熔断&服务降级
服务之间依赖,一个不可用,导致其他的可能也不可用。
熔断:设置服务超时时间,若经常超时到达某个阈值,开启短路,就其他服务不在调用这个服务。
降级:整体把控,高峰,资源紧张期间,让非核心业务降级,简单处理,返回null,返回异常等等
8 API网关
整体架构的重要组件,抽象了微服务中都需要的公共功能,同时提供了客户端负载均衡,服务自动熔断,灰度发布,统一认证,限流控制,日志统计等丰富的功能,帮助我们解决很多API管理难题。
整体服务框架
项目中的数据表,都没有使用外建,因为数据量很大,每添加,删除都要检查外建太麻烦了。
每一个微服务有自己的数据库注意是单独的数据库,不是单独的表。
管理系统的前后端,使用了码云上的人人开源,在vscode中运行npm install升级的时候,会有错误,所以我尝试使用了tyarn
1 | tyarn install |
逆向工程开发(根据数据库,开发出他的代码),也使用人人上的代码。
分布式系统搭建,SpringCloud
此处使用SpringCloud Alibaba
Mysql
1 | 先创建 |
Nacos Server
nacos注册中心服务器
Spring Cloud Alibaba Nacos Discovery
可以使用docker启动
1 | sudo docker run --restart=always -e MODE=standalone --name nacos-test -p 8848:8848 -d nacos/nacos-server:2.0.3 |
然后application.yml中配置
需要有该服务的应用名字
Nacos配置中心
Spring Cloud Alibaba Nacos Config
需要在bootstrap.properties中配置,这个配置文件优先于application.tml文件。
1 | /** |
Open Feign
一个声明式的HTTP客户端
会员服务(里面引入openfeign,那么就有远程调用别的服务的能力)先从注册中心看优惠券服务在哪几台机器,然后注册中心返回在的机器,然后会员服务从里面挑一台,发送请求。
1 | /** |
网关
路由:ID,目的地,集合filters
断言:让开发人员匹配当次请求的任何信息。断言成功,就能到达目的地。
过滤器:请求或者相应都可以被修改,在请求抵达目标或者请求到达响应之后。
请求到达网关,网关利用断言判断这次请求是否符合某个路有规则,若符合,则按照路有规则路由到指定地方,但是到达前需要经过一系列过滤器。
SpringCloud的Gateway,是所有请求的入口
1 比如,同一个服务,一个机器挂了,改到另一个机器;一个状态改到另一个状态;端口改到另一个端口。
所以,给任何服务发送的请求,都先发送给网关,然后动态路由到指定位置。
2 每一个请求过来,可能要加权限,限流,日志输出,把功能加载每个服务上,太多
注意
要让网关转发的服务,需要在注册中心中注册,然后uri的时候写在注册中心注册的应用名,如下面
1 | spring: |
比如 http://localhost:88/api/asdasd.jpg 先到(uri作用) http://DinosaurStore-renrenfast:8080/api/asdasd.jpg (这个8080是在注册中心找到该应用名的服务然后写上去的,此处的DinosaurStore-renrenfast也会变成该应用注册的ip的吧?) 再到(filter)http://DinosaurStore-renrenfast:8080/renren-fast/asdasd.jpg
前端
ES6
ECMAScript6.0是浏览器语言的规范、标准,而js是实现。好比jdbc是标准,mysql是实现。
Vue
MVVM
M:Model,模型,包含数据和一些基本操作
V:View,视图,渲染结果
VM:ViewModel,模型与视图之间的双向操作(无需开发人员干预)
- 只要model发生改变,view上自然表现出来(VM中封装的一些指令操作,自动的改View界面
- 视图界面比如表单等内容改变,自动到Model中(View中的DOM Listeners自动监听)
Vue
Vue很强大,也有计算属性,和监听(监控一个数值的变化,从而做出相应的反应,swift里面类似的语法)
组件化
页面可以划分成很多部分,不同页面也会有相同的部分,比如相同的头部导航。所以吧页面的不同部分拆分成独立的组建,然后在不同的页面就能够共享这些组件,避免重复开发。
DOM
(Document Object Model文档对象模型),网页中的用来表示文档中对象的标准模型,通过javascript可以对网页中的所有DOM对象进行操作。
Element-ui
是一款vue的组件库,一套为开发者、设计师和产品经理准备的基于Vue2.0的桌面端组件库。比如单选框、多选框等很多组件。
导入其他的组件库,使用也是类似
Babel
是一个js编译器,可以使用es的最新语法变成,而不用担心浏览器兼容问题,它会自动转化为浏览器兼容的代码。
Webpack
自动化项目构建工具。gulp也是同类产品。
后台管理系统项目
跨域
浏览器不能执行其他网站的脚本,它是由浏览器的同源策略造成的。同源策略,指协议,域名,端口都要相同,其中有一个不能就会产生跨域。
注意,带src属性的资源不受跨域限制,比如图片验证码等等。简单请求跨域也没关系。
解决跨域
- 使用nginx转化为同一个域。
- 服务器告诉他,能跨域

前端vue的增删改查和后台的增删改查,都可以逆向生成,人人工程。
OSS
把图片先传给后台,后台再传到OSS,这样做带宽占用太大。
1 | /** |
所以考虑:用户向应用服务器请求上传policy,应用服务器给出签名数据,然后用户直接上传数据到OSS
关于校验
在前端,表单输入的数据需要进行校验,不能为空,只能首字母等等,
但是在后端还是要继续校验的,因为如果绕过前端,直接提交,就会出问题了呀,比如使用postman
此处前端校验,使用的是Vue中的功能 validator验证器件。
此处,后端校验,使用的是JSR303
关于抛出异常
可以使用一个controller来处理所有的异常,这个时候抛出的错误code(不只是404这种)是什么意思就很重要了(公司文档统一规定)。
状态码写成枚举类型,放在common的BizCodeEnum枚举中。
@ControllerAdvice可以集中处理所有异常。
@Slf4j记录日志,就可以使用log.err()来进行打印了。
java中for、foreach、stream性能比较
最终总结:如果数据在1万以内的话,for循环效率高于foreach和stream;如果数据量在10万的时候,stream效率最高,其次是foreach,最后是for。另外需要注意的是如果数据达到100万的话,parallelStream异步并行处理效率最高,高于foreach和for。
各个微服务功能
前端:使用Vue框架,使用的是renrenfast-ui这个基础框架
后端:
每一个微服务都可以独立部署,运行,升级,独立自治(数据库、技术、架构、业务等都自治)
1、 renren-fast:提供登录、sidebar侧边栏的增删改、账户管理等一些基础服务
2、store-third-party:提供OSSpolicy签名、
3、store-common:提供一些公共服务,其他微服务会依赖该服务,比如配置中心、注册中心、mysql驱动、lombok等等,校验分组的组别,异常枚举类
4、store-product:商品的三级分类(增删改、拖动等等)、品牌管理(OSS图片上传、后台数据校验)、发布商品、商品维护、商品规格维护等等。
SPU商品、SKU商品规格(比如不同颜色,不同之间内存的组合),一个spuid下可能有多个sku,然后根据spuid的到的attrlist是一个list,每一个sku对应一个list,每一个list里面有该sku的attr。
5、store-ware:商品库存,商品采购,采购单入库,添加到库存。比如,添加采购需求,然后添加采购单(关联人员),然后采购单和采购需求相关联,关联分配后人员在自己的手机app上领取该采购需求和采购单。(采购单一旦领取,采购需求就不能再重新分配了),采购完成后,采购人员在自己的app上点击完成。 此处采购人员点击领取(采购单变成已领取,采购项目变成正在购买),采购人员点击完成(采购单完成,勾选了的采购项目显示完成),这两个任务由postman模拟实现,后台都是实现了的。代码中PurchaseDetailEntity是采购项,采购需求;PurchaseEntity是采购单,里面包含了多个采购项目。
6、store-search:主要是对es进行操作,将数据存放到es中,从里面查询等等,目前实现的是,将如果商品上架,那么将商品存储到es中去。
基础课程总结
分布式基础课
微服务
注册中心,Nacos,放在application.yaml,商品服务可能要调用会员服务以及库存服务等等,所以需要注册中心,而且网关转发的时候,也是指定服务的注册应用名字。 在应用程序上加上@EnableDiscoveryClient。
配置中心,Nacos,放在bootstra.properties,在线上可以直接更改配置文件,而不用重新运行代码
远程调用,使用OpenFeign实现,此处的远程调用,是本服务使用feign给其他服务发送一个远程调用(),首先要开启远程调用(被调用的服务注册到注册中心),导入feign的依赖,开启远程调用功能@EnableFeighClients(basePackages=这里写那个被调用的服务的接口包(里面的每一个类指定那个服务的哪个接口方法,接口上面@FeignClient(“应用名”),里面写被调用服务的controller的方法接口))。
网关,application.yaml中配置路有规则,还有网关出统一的配置,跨域解决。
基础开发
SpringBoot2.0、SpringCloud(开启服务的注册发现,开启feign的远程调用以及接口编写)、Mybatis-Plus(配置简单的包扫描@MapperScan等等)、Vue组件化、阿里云OSS对象存储(第三方使用:申请开通、拿到权限、调用人家的SDK)
环境
Vagrant(快速创建启动虚拟机)、Linux、Docker、MySQL、Redis、逆向工程&人人开源
开发规范
1 数据校验JSR303、全局异常处理(写在store-product中的@RestControllerAdvice(basePackages = “com.jcwang.store.product.controller”))、全局统一返回(R对象)、全局跨域处理(网关中)
2 枚举状态(全部在common中)、业务状态码、VO与TO与PO的划分、逻辑删除@TableLogic(value = “1”, delval = “0”),0表示删除
3 Lombok中,@Data自动生成getset方法,@Slf4j后log.error()就能用了;
还有如果要mysql中的语句进行打印,
1 | logging: |
Elasticsearch
分布式的开源搜索和分析引擎,(MySQL CRUD好,但是搜索在大数据下还是承受不了的)
分布式的实时文件存储,每个字段都被索引并可被搜索
分布式的实时分析搜索引擎–做不规则查询
可以扩展到上百台服务器,处理PB级结构化或非结构化数据
Elasticsearch也使用Java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,但是它的目的是通过简单的RESTful API来隐藏Lucene的复杂性,从而让全文搜索变得简单。
ES是存储在内存中的。
某个 索引 下, 某种 类型 的一个文档,文档是JSON格式的 ,属性
某个 数据库 下, 某个 表 的一行**数据 ** 列
倒排索引
先将整个句子拆分为单词,然后维护 哪个单词 在 哪些记录 里面有。搜索得到之后按照得分概念排序返回,该搜索的几个单词总共命中多少个记录。
全文检索字段用 match,其他非 text 字段匹配用 term。
分词器,比如,whitespace tokenizer 遇到空白字符时分割文本。要分中文,需要自己安装一个ik分词器。
analyzer”: “ik_smart” analyzer”: “ik_max_word”,
1 | sudo docker exec -it d18aaabceba2 /bin/bash |
安装
elasticsearch:7.4.2
kibana:7.4.2,可视化检索数据
1 | sudo mkdir -p /mydata/elasticsearch/config |
PUT和POST区别是,PUT一定要带id,而POST可以不带,自动生成唯一的id。
GET是查询,其中有一个愿数据“_seq_no”,是给乐观锁用的,若一样,这次才修改。修改完之后 _seq_no+1,我们存储的数据在元数据” _source”下面。
1 | http://124.222.48.192:9200/customer/external/2?if_seq_no=3&if_primary_term=1 |
前端访问页面项目
之前的后台管理系统,是Vue前端,所以做到了 前后端分离。
搭建域名访问环境
此处前台网页使用了nginx的渲染
nginx作为反向代理(另外的功能有负载均衡,还有前面的ik分词器),反向代理给网关,网关的好处是可以做统一鉴权认证以及限流工作等等
而加上nginx之后,每一个微服务自己里面的页面,页面需要的静态资源,可以都搬家到nginx里面,做到了部署期间的 动静分离。静态资源(图片,js,css等以实际文件存在的方式)直接由nginx返回,而动态请求发送给微服务拿到动态资源。
正向代理:如科学上网,隐藏客户端的信息,就是被访问到的服务器看到的我的信息是代理的信息。
反向代理:屏蔽内网服务器信息,也就是屏蔽了运行微服务的服务器信息,负载均衡访问。请求商城的时候先来到nginx,由nginx转交给后台对应的服务集群。

注意,下面的操作我是在macOS下做的
1 更改本地hosts,让它知道域名
1 | sudo vim /etc/hosts |

2 docker安装nginx,配置反向代理
2.1 在/Users/dinosaur/mydockerdata/nginx/conf/nginx.conf加入上游服务器,指定有多少
1 | http { |
2.2 然后写/Users/dinosaur/mydockerdata/nginx/conf/conf.d/dinosaurstore.conf文件,使得nginx进行反向代理
1 | server { |

3 在store-gateway中的application.yaml配置网关的路由规则
1 | spring: |
压力测试(压测)
postman用来测试服务。
jmeter用来做性能测试。
jconsole 堆内存的监控、CPU占用率等等。
jvisualvm一样,可以下载插件,查看GC。
优化问题
thyemeleaf开缓存,给数据库加索引,多次查询变一次批量查询,redis缓存,es检索,动静分离静态资源直接从nginx中拿到,若多了从nginx到网关再到tomcat这两步也会增加响应时间。
动静分离
首先将idea中的static下面的放到nginx的html/static下面,然后修改idea中的index中的一些资源的路径(加上/static/)
然后配置nginx,让访问的静态资源,都到nginx自己的目录下面找。就是跟反向代理一样的配置。
1 | server { |
Author: Jcwang
Permalink: http://example.com/2022/06/22/%E9%A1%B9%E7%9B%AEDinosaurStore/