转专业的动力+本科出身不错不想低头+时不我待+热爱+弥补诸多遗憾+赚钱,其中时不我待的感觉尤为强烈,太一言难尽了。自己也想像很多同学那样过上平凡轻松的生活,但我的境地如果那样选择无异于zs,结果就是被迫疯狂卷自己(不卷别人)。
表面上我学了这么多框架和源码,看似真的只有广度没有深度?但是其实我很多内容都看了好几遍。
spring源码大致流程(getBean都看过不下十遍了,动态代理和三级缓存、组件配置啥的更不在话下)+Redis源码书+分布式事务+Tomcat源码+Netty+NIO+JUC+JVM+k8s+rabbitMQ+KafkaMQ+SpringCloud+ZK+设计模式+Mysql(MVCC、各种log)+Shiro。另外JSP、Vue、maven等必备技能 另外,我最擅长的其实是socket及NIO底层相关,我觉得这门技术也是我阿里一面聊一个小时最后拿sp的原因。
结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 gulimall ├── gulimall-common -- 工具类及通用代码 ├── renren-generator -- 人人开源项目的代码生成器 ├── gulimall-auth-server -- 认证中心(社交登录、OAuth2.0、单点登录) ├── gulimall-cart -- 购物车服务 ├── gulimall-coupon -- 优惠卷服务 ├── gulimall-gateway -- 统一配置网关 ├── gulimall-order -- 订单服务 ├── gulimall-product -- 商品服务 ├── gulimall-search -- 检索服务 ├── gulimall-seckill -- 秒杀服务 ├── gulimall-third-party -- 第三方服务 ├── gulimall-ware -- 仓储服务 └── gulimall-member -- 会员服务
技术选型 后端技术
前端技术
架构图 系统架构图
业务架构图
环境搭建 开发工具
开发环境
注意:以上的除了jdk都是采用docker方式进行安装,详细安装步骤可参考百度!!!
分布式基础概念 集群是个物理形态,分布式是个工作方式。
远程调用:在分布式系统中,各个服务可能处于不同主机,但是服务之间不可避免的相互调用,我们称为远程调用
springcloud中使用HTTP+JSON的方式完成远程调用
A服务调用B服务,A服务并不知道B服务当前在哪几台服务器有,那些是正常的,那些服务已经下线。解决这个问题可以引入注册中心。
配置中心用来几种管理微服务的配置信息。
在微服务架构中,微服务之间通过网络进行通信,存在相互依赖,当其中一个服务不可用时,有可能会造成雪崩效应。要防止这样的情况,必须要有容错机制来保护服务。
rpc远程调用情景:
订单服务 –> 商品服务 –> 库存服务
库存服务出现故障导致响应慢,导致商品服务需要等待,可能等到10s后库存服务才能响应。库存服务的不可用导致商品服务阻塞,商品服务等的期间,订单服务也处于阻塞。一个服务不可用导致整个服务链都阻塞。如果是高并发,第一个请求调用后阻塞10s得不到结果,第二个请求直接阻塞10s。更多的请求进来导致请求积压,全部阻塞,最终服务器的资源耗尽。导致雪崩
解决方案:
1 服务熔断
指定超时时间,库存服务3s没有响应就超时,如果经常失败,比如10s内100个请求都失败了。开启断路保护机制,下一次请求进来不调用库存服务了,因为上一次100%错误都出现了,我们直接在此中断,商品服务直接返回,返回一些默认数据或者null,而不调用库存服务了,这样就不会导致请求积压。
设置服务的超时,当被调用的服务经常失败到达某个阈值,我们可以开启断路保护机制,后来的请求不再去调用这个服务。本地直接返回默认的数据 2 服务降级
在运维期间,当系统处于高峰期,系统资源紧张,我们可以让非核心业务降级运行。降级:某些服务不处理,或者处理简单【抛异常、返回NULL、调用Mock数据、调用Fallback处理逻辑】 API网关
客户端发送请求到服务器路途中,设置一个网关,请求都先到达网关,网关对请求进行统一认证(合法非法)和处理等操作。他是安检。
在微服务架构中,API gateway作为整体架构的重要组件,它抽象了微服务中都需要的公共功能,同时提供了客户端负载均衡,服务自动熔断,灰度发布,统一认证,限流流控,日志统计等丰富的功能,帮助我们解决很多API管理难题。
搭建步骤 虚拟机固定ip 1 2 https://www.cnblogs.com/telwanggs/p/10882369.html ipv4的网络设置要和 vmware 网络编辑器里面的ip一致
防火墙 1 2 3 4 5 6 7 8 l 查看防火墙状态(systemctl status firewalld、firewall-cmd --state) l 暂时关闭防火墙(systemctl stop firewalld) l 永久关闭防火墙(systemctl disable firewalld) l 开启防火墙(systemctl start firewalld) l 开放指定端口(firewall-cmd --zone=public --add-port= 8080 /tcp --permanent) l 关闭指定端口(firewall-cmd --zone=public --remove-port= 8080 /tcp --permanent) l 立即生效(firewall-cmd --reload) l 查看开放的端口(firewall-cmd --zone=public --list-ports)
docker安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 https://docs.docker.com/engine/install/centos/ sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo systemctl start docker sudo systemctl start docker 阿里云加速 https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://y1xrkxwk.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl restart docker 1、查看已启动镜像:docker images 查看已运行容器:docker ps 2、查看所有镜像:docker images -a 3、启动docker:sudo systemctl start docker 4、虚拟机开机启动:sudo systemctl enable docker 5、设置自动启动容器:sudo docker update mysql --restart=always 6、启动已存在的容器或重启容器,例: 1)查看容器的id或name:docker ps -a 2)重启restart id或name【重启就代表启动了】: docker restart 1b4671904bfa docker restart mysql 7、终止容器:docker stop redis 8、删除容器:docker rm redis 9、进入容器的运行时环境 进入mysql:docker exec -it mysql /bin/bash 进入redis:docker exec -it redis redis-cli 进入redis:docker exec -it redis /bin/sh whereis mysql 10、退出容器运行时环境:exit 11、虚拟机开机自动启动mysql:sudo docker update mysql --restart=always
mysql 安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 docker pull mysql:5.7 sudo docker run -p 3306:3306 --name mysql \ -v /mydata/mysql/log:/var/log/mysql \ -v /mydata/mysql/data:/var/lib/mysql \ -v /mydata/mysql/conf:/etc/mysql \ -e MYSQL_ROOT_PASSWORD=root \ -d mysql:5.7 参数: ● -p 3306:3306:将容器的3306端口映射到主机的3306端口 ● --name:给容器命名 ● -v /mydata/mysql/log:/var/log/mysql:将配置文件挂载到主机/mydata/.. ● -e MYSQL_ROOT_PASSWORD=root:初始化root用户的密码为root 修改配置 vi /mydata/mysql/conf/my.cnf [client] default-character-set=utf8 [mysql] default-character-set=utf8 [mysqld] init_connect='SET collation_connection = utf8_unicode_ci' init_connect='SET NAMES utf8' character-set-server=utf8 collation-server=utf8_unicode_ci skip-character-set-client-handshake skip-name-resolve lower_case_table_names=1 lower_case_table_names=1在linux下默认是0 指的是是否大小写敏感 1 表示存在磁盘是小写 比较的时候不区分大小写
redis 安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 创建redis配置文件目录 mkdir -p /mydata/redis/conf touch /mydata/redis/conf/redis.conf 启动redis容器 docker run -p 6379:6379 --name redis \ -v /mydata/redis/data:/data \ -v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \ -d redis redis-server /etc/redis/redis.conf redis持久化 echo "appendonly yes" >> /mydata/redis/conf/redis.conf # 重启生效 docker restart redis docker update redis --restart=always
maven 配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 下载 https://maven.apache.org/download.cgi 配置环境变量 设置阿里云镜像 <mirror> <id>alimaven</id> <mirrorOf>central</mirrorOf> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/repositories/central/</url> </mirror>
mongo安装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 http://www.zhou-kang.cn/article/37 #拉取镜像 docker pull mongo:4.0.3 #创建容器 docker create --name mongodb -p 27017:27017 -v /mydata/mongodb/data:/data/db mongo:4.0.3 --auth #启动容器 docker start mongodb-server #进入容器 docker exec -it mongodb-server /bin/bash #进入admin数据库 mongo use admin #添加管理员,其拥有管理用户和角色的权限 db.createUser({ user: 'lwj', pwd: '123456', roles: [ { role: "root", db: "admin" } ] }) #使用quit()退出mogondb > quit() #进行认证 mongo -u "hang" -p "123456" --authenticationDatabase "admin"
nacos 集成 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 https://blog.csdn.net/gongzi_9/article/details/123373981 No Feign Client for loadBalancing defined. Did you forget to include 1. 安装依赖 2. 下载启动服务 需要java环境 一定要配置JAVA_HOME环境变量 3. 配置服务地址 name 4. 注解 开启服务注册 nacos config 1. 配置服务 spring: application: name: gulimall-coupon cloud: nacos: config: server-addr: 127.0.0.1:8848 file-extension: yml file-extension:如果不是默认的一定要加上 对应的取值一定要也对 如果是高版本 bootstrap依赖
远程调用 1 2 3 4 5 1. 安装依赖 Feign 2. 编写服务接口 2.1. 注解配置要调用的服务@FeignClient("gulimall-coupon") 2.2. 复制要调用服务方法的完整签名 3. 注解开启远程服务 并配置接口的包名 @EnableFeignClients(basePackages = "com.gulimall.member.feign")
renrenfast 网关配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 安装依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-loadbalancer</artifactId> <version>3.1.6</version> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> 配置网关 - id: route_admin uri: lb://renrenfast # lb 表示负载均衡 predicates: - Path=/api/** # 默认前端都带上api前缀 filters: - RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment} 解决跨域 新建配置类 @Configuration public class GulimailCorsConfiguration { @Bean public CorsWebFilter corsWebFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); //1、配置跨域 corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.addAllowedOriginPattern("*"); corsConfiguration.setAllowCredentials(true); source.registerCorsConfiguration("/**",corsConfiguration); return new CorsWebFilter(source); } } java.lang.IllegalArgumentException: When allowCredentials is true, allowedOrigins cannot contain the special value "*" since that cannot be set on the "Access-Control-Allow-Origin" response header. To allow credentials to a set of origins, list them explicitly or consider using "allowedOriginPatterns" instead. 2.4.0之后要用allowedOriginPatterns
商品分类开发 1 2 3 4 5 6 7 8 9 10 11 12 13 逻辑删除 https://baomidou.com/pages/6b03c5/ 添加配置 mybatis-plus: global-config: db-config: logic-delete-field: flag # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2) logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) 实体类字段上加上@TableLogic注解 @TableLogic private Integer deleted;
JSR303 数据校验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.2.2.RELEASE</version> <type>pom</type> </dependency> 实体类添加注解 譬如 @NotBland 接口开启校验 @Validated 封装 通用code public enum BizCodeEnume { UNKNOW_EXCEPTION(10000,"系统异常"), VAILD_EXCEPTION(10001,"系统异常"); private int code; private String msg; BizCodeEnume(int code,String message) { this.code = code; this.msg = message; } public int getCode() { return code; } public String getMsg() { return msg; } } 封装统一异常处理 @Slf4j @RestControllerAdvice(basePackages = "com.gulimall.product.controller") public class GulimallExceptionControllerAdvice { @ExceptionHandler(value = MethodArgumentNotValidException.class) public R handleVaildException(MethodArgumentNotValidException e) { log.error("数据校验出现异常{},异常类型:{}",e.getMessage(),e.getClass()); Map<String,String> map = new HashMap<>(); e.getBindingResult().getFieldErrors().forEach(item->{ map.put(item.getField(),item.getDefaultMessage()); }); return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data",map); } @ExceptionHandler(value = Throwable.class) public R handleException(Throwable throwable){ return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMsg()); } }
SPU SKU 1 2 3 4 5 6 7 简介: SPU:标准化产品单元 standard product unit,是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。 SKU:库存量单位 Stock Keeping Unit, SPU:iphone XS、iphone XS max、iphone XR、MI8、 SKU:iphonex 64G 黑曜石、MI8 8+64G+黑色
业务开发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 字段为空不返回的字段 @JsonInclude(JsonInclude.Include.NON_EMPTY) @TableField(exist = false) private List<CategoryEntity> children; mybatis 配置分页组件 @Configuration @EnableTransactionManagement //开启使用 @MapperScan("com.xunqi.gulimall.product.dao") public class MyBatisConfig { //引入分页插件 @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false paginationInterceptor.setOverflow(true); // 设置最大单页限制数量,默认 500 条,-1 不受限制 paginationInterceptor.setLimit(1000); return paginationInterceptor; } } 关键字查询 public PageUtils queryPage(Map<String, Object> params) { String key = (String) params.get("key"); QueryWrapper<BrandEntity> wrapper = new QueryWrapper<BrandEntity>(); if (!StringUtils.isEmpty(key)) { wrapper.eq("brand_id",key).or().like("name",key); } IPage<BrandEntity> page = this.page( new Query<BrandEntity>().getPage(params), wrapper ); return new PageUtils(page); } xml 查询 <update id="updateCategory"> UPDATE `pms_category_brand_relation` SET catelog_name=#{name} WHERE catelog_id=#{catId} </update>
vo object划分
PO(persistant object) 持久对象 对应数据库中的一条记录 不包含任何对数据库的操作
DO(Domain Object) 领域对象 从现实世界抽象出来的有形或者无形的业务实体
TO(Transfer Object)数据传输对象 不同应用程序之间传输的对象 微服务之间 封装传输的对象
DTO
VO 值对象 视图对象 通常用于业务层之间的数据传输 和po一样仅仅包含数据而已 但应该是抽象出来的业务对象 接收页面传递来的数据 封装对象 将业务处理完成的对象 封装成页面需要的数据
BO(business object) 业务对象
BO 可以包含PO 这样处理业务逻辑 可以针对BO去处理
POJO
DAO
1 连表查询是非常危险 假设有10万个属性 1000 个分组 迪卡尔积就是十个亿中间表数据 这是非常可怕的
连表查询 更新 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 public PageUtils queryBaseAttrPage(Map<String, Object> params, Long catelogId, String attrType) { QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<AttrEntity>() .eq("attr_type","base".equalsIgnoreCase(attrType) ? ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() : ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode()); //根据catelogId查询信息 if (catelogId != 0) { queryWrapper.eq("catelog_id",catelogId); } String key = (String) params.get("key"); if (!StringUtils.isEmpty(key)) { //attr_id attr_name queryWrapper.and((wrapper) -> { wrapper.eq("attr_id",key).or().like("attr_name",key); }); } IPage<AttrEntity> page = this.page( new Query<AttrEntity>().getPage(params), queryWrapper ); PageUtils pageUtils = new PageUtils(page); List<AttrEntity> records = page.getRecords(); List<AttrRespVo> respVos = records.stream().map((attrEntity) -> { AttrRespVo attrRespVo = new AttrRespVo(); BeanUtils.copyProperties(attrEntity, attrRespVo); //设置分类和分组的名字 if ("base".equalsIgnoreCase(attrType)) { AttrAttrgroupRelationEntity relationEntity = relationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id",attrEntity.getAttrId())); if (relationEntity != null && relationEntity.getAttrGroupId() != null) { AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(relationEntity.getAttrGroupId()); attrRespVo.setGroupName(attrGroupEntity.getAttrGroupName()); } } CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId()); if (categoryEntity != null) { attrRespVo.setCatelogName(categoryEntity.getName()); } return attrRespVo; }).collect(Collectors.toList()); pageUtils.setList(respVos); return pageUtils; } AttrEntity attrEntity = new AttrEntity(); BeanUtils.copyProperties(attr,attrEntity); this.updateById(attrEntity); if (attrEntity.getAttrType() == ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode()) { //1、修改分组关联 AttrAttrgroupRelationEntity relationEntity = new AttrAttrgroupRelationEntity(); relationEntity.setAttrGroupId(attr.getAttrGroupId()); relationEntity.setAttrId(attr.getAttrId()); Integer count = relationDao.selectCount(new QueryWrapper<AttrAttrgroupRelationEntity>() .eq("attr_id", attr.getAttrId())); if (count > 0) { relationDao.update(relationEntity, new UpdateWrapper<AttrAttrgroupRelationEntity>().eq("attr_id",attr.getAttrId())); } else { relationDao.insert(relationEntity); } }
批量删除 1 2 3 4 5 6 <delete id="deleteBatchRelation"> DELETE FROM pms_attr_attrgroup_relation WHERE <foreach collection="entities" item="item" separator="OR"> (attr_id=#{item.attrId} AND attr_group_id=#{item.attrGroupId}) </foreach> </delete>
controller 1 2 3 1、 controller处理请求 接收和校验数据 2、 service 结束controller传来的数据 进行业务处理 3、 controller 接收service处理完的数据 封装成页面需要的vo
springboot 报错之循环引用 1 2 Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true. 解决办法加入 @Lazy 注解
elasticsearch、kibana安装
拉取1 2 docker pull elasticsearch:7.4.2 docker pull kibina:7.4.2
挂载目录配置1 2 mkdir -p /mydata/elasticsearch/config mkdir -p /mydata/elasticsearch/data
启动Elastic search1 2 3 4 5 6 7 docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \ -e "discovery.type=single-node" \ -e ES_JAVA_OPTS="-Xms64m -Xmx512m" \ -v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \ -v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \ -v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \ -d elasticsearch:7.4.2
设置开机启动elasticsearch
1 docker update elasticsearch --restart=always
(4)启动kibana:
1 docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.187.130:9200 -p 5601:5601 -d kibana:7.4.2
设置开机启动kibana
1 docker update kibana --restart=always
(5)测试
查看elasticsearch版本信息: http://192.168.187.130:9200/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { "name" : "63d82eeec0d3" , "cluster_name" : "elasticsearch" , "cluster_uuid" : "j0meIrlySGGXjd-8tkEPNg" , "version" : { "number" : "7.4.2" , "build_flavor" : "default" , "build_type" : "docker" , "build_hash" : "2f90bbf7b93631e52bafb59b3b049cb44ec25e96" , "build_date" : "2019-10-28T20:40:44.881551Z" , "build_snapshot" : false , "lucene_version" : "8.2.0" , "minimum_wire_compatibility_version" : "6.8.0" , "minimum_index_compatibility_version" : "6.0.0-beta1" } , "tagline" : "You Know, for Search" }
显示elasticsearch 节点信息http://192.168.187.130:9200/_cat/nodes ,
1 127.0 .0 .1 76 95 1 0.26 1.40 1.22 dilm * 0 adeb7852e00
访问Kibana: http://192.168.137.14:5601/app/kibana
测试数据 https://github.com/elastic/elasticsearch/blob/v6.8.18/docs/src/test/resources/accounts.json
下载工具安装 yum -y install wget
压缩工具安装 yum install zip unzip
plugin目录安装分词器 1 2 3 wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip unzip elasticsearch-analysis-ik-7.4.2.zip -d ik docker restart elasticsearch