谷粒商城-基础篇-环境搭建(P1-P44)

文章目录


提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、项目简介

谷粒商场:

  • 项目采用SpringCloudAlibaba技术栈。由业务集群系统+后台管理系统构成,打通了微服务分布式开发及全栈开发技能,包含前后分离全栈开发、Restful接口、数据校验、网关、注册发现、配置中心、熔断、限流、降级、链路追踪、性能监控、压力测试、系统预警、集群部署、持续集成、持续部署…

  • 包括前台商城系统以及后台管理系统,基于 SpringCloud + SpringCloudAlibaba + MyBatis-Plus等技术实现。 采用 Docker 容器化部署,前后端分离开发。前台商城系统包括:用户登录、注册;商品搜索、商品详情、购物车、下订单流程、秒杀活动等模块…

二、分布式基础概念

1、微服务

微服务架构风格,就像是把一个单独的应用程序开发为一套小服务,每个小服务运行在自己的进程中,并使用轻量级机制通信,通常是HTTP API。这些服务围绕业务能力来构建,并通过完全自动化部署机制来独立部署。这些服务使用不同的编程语言书写,以及不同数据存储技术,并保持最低限度的集中式管理。

简而言之:拒绝大型单体应用,基于业务边界进行服务微化拆分,各个服务独立部署运行。

2、集群&分布式&节点

集群是个物理形态,分布式是个工作方式。
集群是指将几台服务器集中在一起,实现统一业务。
分布式是指将不同的业务分布在不同的地方。

节点:集群中的一个服务器。 分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像是单个相关系统。
分布式中的每一个节点,都可以叫集群。而集群不一定就是分布式的。

3、远程调用

在分布式系统中,各个服务可能处于不同主机,但是服务之间不可避免的需要互相调用,我们称之为远程调用。
Spring Cloud 中使用 HTTP + JSON 的方式完成远程调用。

4、负载均衡

  • 合理分配服务器系统资源。

常见负载均衡算法:
轮询: 按顺序依次循环调用。
最小连接: 优先选择连接数量最少的,也就是压力最小的后端服务器,在会话较长的情况下可以考虑采取这种方式。
散列: 根据请求源的 IP 的散列来选择要转发的服务器。这种方式可以一定程度上保证特定用户能连接到相同的服务器。如果你的应用需要处理状态而要求用户能连接到和之前相同的服务器,可以考虑采取这种方式。

5、服务注册/发现&注册中心

A 服务调用 B 服务,A 服务并不知道 B 服务当前在哪几台服务器有,哪些正常,哪些服务已经下线。解决这个问题引入注册中心。如果某些服务下线,我们其他人可以实时的感知到其他服务的状态,从而避免调用不可用的服务。

6、配置中心

集中管理微服务的配置信息

7、服务熔断&服务降级

服务熔断: 设置服务的超时,当被调用的服务经常失败到达某个阈值,我们可以开启断路保护机制,后来的请求不再去调用这个服务。本地直接返回默认的数据。

服务降级: 在运维期间,当系统处于高峰期,系统资源紧张,我们可以让非核心业务降级运行。降级:某些业务不处理,或者简单处理【抛异常、返回 Null 、返回 Mock 数据、调用 Fallback 处理逻辑】

8、API 网关

在微服务架构中,API Gateway 作为整体架构的重要组件,它抽象了微服务中都需要的公共功能,同时提供了客户端负载均衡、服务自动熔断、灰度发布、统一认证、限流流控、日志统计等丰富的功能,帮助我们解决了很多 API 管理难题。

微服务架构图
谷粒商城微服务架构图

  • 前后分离开发,分为内网部署和外网部署,

外网是面向公众访问的,部署前端项目,可以有手机APP,电脑网页;

内网部署的是后端集群,前端在页面上操作发送请求到后端,在这途中会经过Nginx集群,Nginx把请求转交给API网关(springcloud gateway)(网关可以根据当前请求动态地路由到指定的服务,看当前请求是想调用商品服务还是购物车服务还是检索),从路由过来如果请求很多,可以负载均衡地调用商品服务器中一台(商品服务复制了多份),当商品服务器出现问题也可以在网关层面对服务进行熔断或降级(使用阿里的sentinel组件),网关还有其他的功能如认证授权、限流(只放行部分到服务器)等。

  • 到达服务器后进行处理(springboot为微服务),服务与服务可能会相互调用(使用OpenFeign组件),有些请求可能经过登录才能进行(基于OAuth2.0的认证中心。安全和权限使用springSecurity控制)

  • 服务可能保存了一些数据或者需要使用缓存,我们使用redis集群(分片+哨兵集群)。持久化使用mysql,读写分离和分库分表。

  • 服务和服务之间会使用消息队列(RabbitMQ),来完成异步解耦,分布式事务的一致性。有些服务可能需要全文检索,检索商品信息,使用ElaticSearch。

  • 服务可能需要存取数据,使用阿里云的对象存储服务OSS。

  • 项目上线后为了快速定位问题,使用ELK对日志进行处理,使用LogStash收集业务里的各种日志,把日志存储到ES中,用Kibana可视化页面从ES中检索出相关信息,帮助我们快速定位问题所在。

  • 在分布式系统中,由于我们每个服务都可能部署在很多台机器,服务和服务可能相互调用,就得知道彼此都在哪里,所以需要将所有服务都注册到注册中心。服务从注册中心发现其他服务所在位置(使用阿里Nacos作为注册中心)。

  • 每个服务的配置众多,为了实现改一处配置相同配置就同步更改,就需要配置中心,也使用阿里的Nacos,服务从配置中心中动态取配置。

  • 服务追踪,追踪服务调用链哪里出现问题,使用springcloud提供的Sleuth、Zipkin、Metrics,把每个服务的信息交给开源的Prometheus进行聚合分析,再由Grafana进行可视化展示,提供Prometheus提供的AlterManager实时得到服务的告警信息,以短信/邮件的方式告知服务开发人员。

  • 还提供了持续集成和持续部署。项目发布起来后,因为微服务众多,每一个都打包部署到服务器太麻烦,有了持续集成后开发人员可以将修改后的代码提交到github,运维人员可以通过自动化工具Jenkins Pipeline将github中获取的代码打包成docker镜像,最终是由k8s集成docker服务,将服务以docker容器的方式运行。

微服务划分图

在这里插入图片描述

反映了需要创建的微服务以及相关技术。

前后分离开发。前端项目分为admin-vue(工作人员使用的后台管理系统)、shop-vue(面向公众访问的web网站)、app(公众)、小程序(公众)

  • 商品服务:商品的增删改查、商品的上下架、商品详情
  • 支付服务
  • 优惠服务
  • 用户服务:用户的个人中心、收货地址
  • 仓储服务:商品的库存
  • 秒杀服务:定时任务与redis
  • 订单服务:订单增删改查、验价、幂等性token
  • 检索服务:商品的检索ES
  • 中央认证服务:登录、注册、单点登录、社交登录
  • 购物车服务:redis
  • 后台管理系统:添加优惠信息等

三、linux环境搭建

虚拟机安装docker

#卸载系统之前的docker 
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 yum install docker-ce docker-ce-cli containerd.io

sudo systemctl start docker
# 设置开机自启动
sudo systemctl enable docker

docker -v
sudo docker images



  • 配置镜像加速
    https://cr.console.aliyun.com/cn-qingdao/instances/mirrors
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://chqac97z.mirror.aliyuncs.com"]
}
EOF

sudo systemctl daemon-reload
sudo systemctl restart docker

安装mysql

sudo docker pull mysql:5.7

# --name指定容器名字 -v目录挂载 -p指定端口映射  -e设置mysql参数 -d后台运行
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

# 进入已启动的容器
docker exec -it mysql bin/bash
# 退出进入的容器
exit;

因为有目录映射,所以我们可以直接在镜像外执行
vi /mydata/mysql/conf/my.conf 

[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

保存(注意评论区该配置不对,不是collection而是collation)

docker restart mysql

安装redis

如果直接挂载的话docker会以为挂载的是一个目录,所以我们先创建一个文件然后再挂载,在虚拟机中。

# 在虚拟机中
mkdir -p /mydata/redis/conf
touch /mydata/redis/conf/redis.conf

docker pull 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客户端。
docker exec -it redis redis-cli

默认是不持久化的。在配置文件中输入appendonly yes,就可以aof持久化了。修改完docker restart redis,docker -it redis redis-cli

vim /mydata/redis/conf/redis.conf
# 插入下面内容
appendonly yes
保存

docker restart redis

设置redis容器在docker启动的时候启动

docker update redis --restart=always

四、数据库

课件提供了sql文件

五、人人项目构建

renren-fast后台管理系统

renren-fast-vue

由于是第一次打开,使用npm install下载前端项目运行需要的各种环境:

1.npm install
出现以下错误信息:

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! chromedriver@2.27.2 install: `node install.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the chromedriver@2.27.2 install script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\DELL\AppData\Roaming\npm-cache\_logs\2021-12-04T07_35_55_129Z-debug.log

应该是镜像拉取的时候被墙屏蔽了,使用下面命令进行解决:

npm install chromedriver@2.27.2 --ignore-scripts

package.json相当于pom文件,设置版本号。node_modules是用于存放下载的组件

  1. npm run dev
    出现以下错误信息:
ERROR  Failed to compile with 6 errors                                                                                                                                                                                 下午3:47:18

error  in ./src/assets/scss/index.scss

Module build failed: Error: Cannot find module 'node-sass'

解决方案:

  • 使用npm install node-sass --save命令
npm install node-sass --save
  • 使用这个命令报错的话,就是用淘宝镜像的命令
npm install -g cnpm --registry=https://registry.npm.taobao.org

cnpm install node-sass --save
  • 执行cnpm install node-sass --save出现以下错误:
cnpm : 无法加载文件 C:\Users\DELL\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
所在位置 行:1 字符: 1
cnpm install node-sass --save

CategoryInfo          : SecurityError: (:) [],PSSecurityException

FullyQualifiedErrorId : UnauthorizedAccess

解决方案:

1.以管理员身份运行Windows POWERSHELL

2.输入set-ExecutionPolicy RemoteSigned指令

3.输入A

4.再次执行cnpm install node-sass --save命令

成功运行将会出现如下信息:
在这里插入图片描述

六、逆向工程搭建

renren-generator

git clone https://gitee.com/renrenio/renren-generator.git

下载到桌面后,同样把里面的.git文件删除,然后移动到我们IDEA项目目录中,同样配置好pom.xml

刷新maven出现以下错误信息:

'parent.relativePath' of POM io.renren:renren-generator:1.0.0 (D:\IdeaProject\gulimall\renren-generator\pom.xml) points at com.atguigu.gulimall:gulimall instead of org.springframework.boot:spring-boot-starter-parent, please verify your project structure @ line 11, column 10
It is highly recommended to fix these problems because they threaten the stability of your build.
For this reason, future Maven versions might no longer support building such malformed projects.

解决方案:添加 < relativePath/>

ss

1.修改application.yml

url: jdbc:mysql://192.168.56.10:3306/gulimall-pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root

2.然后修改generator.properties

#代码生成器配置
mainPath=com.ljn
#包名
package=com.ljn.gulimall
moduleName=coupon
#作者
author=ljn
#Email
email=1046016768@qq.com
# 表前缀(添加后,类名不会包含表前缀)
# 由于gulimall_product数据库中的表都是以pms为前缀的,所以tablePrefix为pms
tablePrefix=sms_

生成代码的路径名为:package+moduleName
启动项目,登录localhost:80,生成代码,并将生成的代码进行替换,并改正代码错误

3.创建common模块

在common项目的pom.xml中添加

  <dependencies>
        <!-- mybatisPLUS-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.3.2</version>
        </dependency>

        <!--简化实体类,用@Data代替getset方法-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>

        </dependency>

        <!-- httpcomponent包。发送http请求 -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpcore</artifactId>
            <version>4.4.13</version>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

        <!--        导入mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>


    </dependencies>

我们把每个微服务里公共的类和依赖放到common里。

然后在product等所有项目模块中的pom.xml中加入下面内容,作为common的子项目

<dependency>
    <groupId>com.atguigu.gulimall</groupId>
    <artifactId>gulimall-common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

4.测试product

1.在product项目的resources目录下新建application.yml

spring:
  datasource: #配置数据源
    driver-class-name: com.mysql.cj.jdbc.Driver #配置驱动
    url: jdbc:mysql://192.168.10.129:3306/gulimall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai #配置url
    username: root
    password: root


mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml  #配置mybatis-plus扫描映射文件的位置
  global-config:
    db-config:
      id-type: auto   #配置主键自增             ,

2.然后在主启动类上加上注解@MapperScan()

/**
 * 1.整合mybatis-plus
 *      1.导入依赖
 *      2.配置:
 *          1.配置数据源
 *              1.导入数据库驱动(在common中)
 *              2.在application.yml中配置数据源相关信息
 *          2.配置mybatis-plus相关信息
 *              1.使用@MapperScan 注解
 *              2.告诉mybatis-plus,sql映射文件位置
 */
@MapperScan("com.ljn.gulimall.product.dao")
@SpringBootApplication
public class GulimallProductApplication {

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

}

3.测试

@SpringBootTest
class gulimallProductApplicationTests {
    @Autowired
    BrandService brandService;

    @Test
    void contextLoads() {
        BrandEntity brandEntity = new BrandEntity();
        brandEntity.setDescript("哈哈1哈");
        brandEntity.setName("华为");
        brandService.save(brandEntity);
        System.out.println("保存成功");
    }
}

在数据库中就能看到新增数据了

5. 使用renren-generator生成剩余服务的代码

如:生成coupon服务代码

  • 1、修改renren-generator的generator.properties
# 主目录
mainPath=com.atguigu
#包名
package=com.atguigu.gulimall
#模块名--修改的地方
moduleName=coupon
#作者
autho=hh
#email
email=55333@qq.com
#表前缀(类名不会包含表前缀) # 我们的pms数据库中的表的前缀都pms
# 如果写了表前缀,每一张表对于的javaBean就不会添加前缀了
tablePrefix=sms_     #这里也修改了

  • 2、修改yml数据库信息
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://192.168.56.10:3306/gulimall-sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    driver-class-name: com.mysql.cj.jdbc.Driver

mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
      logic-delete-value: 1
      logic-not-delete-value: 0

server:
  port: 7000

3.让coupon也依赖于common,修改pom.xml

<dependency>
            <groupId>com.ljn.gulimall</groupId>
            <artifactId>gulimall-commom</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

启动生成RenrenApplication.java,运行后去浏览器80端口查看,同样让他一页全显示后选择全部后生成。生成后解压复制到coupon项目对应目录下。

6.配置coupon服务的application.yaml

spring:
  datasource: #配置数据源
    driver-class-name: com.mysql.cj.jdbc.Driver #配置驱动
    url: jdbc:mysql://192.168.10.129:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai #配置url
    username: root
    password: root


mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml  #配置mybatis-plus扫描映射文件的位置
  global-config:
    db-config:
      id-type: auto   #配置主键自增             ,

5.启动coupon服务
访问http://localhost:8080/coupon/coupon/list
在这里插入图片描述

七、SpringCloud Alibaba简介

在这里插入图片描述

1、技术搭配方案

  • SpringCloud Alibaba-Nacos:注册中心(服务注册与发现)
  • SpringCloud Alibaba-Nacos:配置中心
  • SpringCloud-Ribbon:负载均衡
  • SpringCloud-Feign:声明式HTTP客户端(调用远程服务)
  • SpringCloud Alibaba-Sentinel:服务容错(限流、降级、熔断)
  • SpringCloud -GateWay:API网关(webflux编程模式)
  • SpringCloud -Sleuth:调用监控链
  • SpringCloud Alibaba-Seata:原Fescar,即分布式事务解决方案

2、环境搭建

在common模块中引入依赖,在common的pom.xml中加入
下面是依赖管理,相当于以后再dependencies里引spring cloud alibaba就不用写版本号, 全用dependencyManagement进行管理


<dependencyManagement>
     <dependencies>
         <dependency>
             <groupId>com.alibaba.cloud</groupId>
             <artifactId>spring-cloud-alibaba-dependencies</artifactId>
             <version>2.1.0.RELEASE</version>
             <type>pom</type>
             <scope>import</scope>
         </dependency>
     </dependencies>
</dependencyManagement>

3、Nacos

一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。
作为我们的注册中心和配置中心。

先了解一下 Spring Cloud 应用如何接入 Nacos Discovery。

1.首先,修改 common中的pom.xml 文件,引入 Nacos Discovery Starter。

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

2.启动Nacos

启动命令windows:startup.cmd -m standalone

3.配置Nacos注册中心地址和服务注册名

在应用的application.yml 配置文件中配置Nacos地址

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
  application:
   	name: gulimall-coupon

4.使用 @EnableDiscoveryClient 注解开启服务注册与发现功能

在服务的启动类中加上 @EnableDiscoveryClient 注解(如coupon)

@EnableDiscoveryClient
@SpringBootApplication
public class GulimallCouponApplication {

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

其他服务配置步骤相同

4.Feign远程调用

1.Feign声明式远程调用

  • feign是一个声明式的HTTP客户端,他的目的就是让远程调用更加简单。给远程服务发的是HTTP请求。
  • 提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数、格式、地址等信息
  • fegion整合了Ribbon(负载均衡)和Hystrix(服务熔断)

2.测试member和coupon的远程调用

想要获取当前会员领取到的所有优惠券。先去注册中心找优惠券服务,
注册中心调一台优惠券服务器给会员,会员服务器发送请求给这台优
惠券服务器,然后对方响应。

  • 服务请求方发送了2次请求,先问nacos要地址,然后再请求
1.会员服务想要远程调用优惠券服务,只需要给会员服务里引入openfeign依赖,他就有了远程调用其他服务的能力。
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.在coupon中修改如下的内容

在coupon服务的CouponController类中

@RequestMapping("coupon/coupon")
public class CouponController {
    @Autowired
    private CouponService couponService;

    @RequestMapping("/member/list")
    public R membercoupons(){    //全系统的所有返回都返回R
        // 应该去数据库查用户对于的优惠券,但这个我们简化了,不去数据库查了,构造了一个优惠券给他返回
        CouponEntity couponEntity = new CouponEntity();
        couponEntity.setCouponName("满100-10");//优惠券的名字
        // 放入一个coupons
        return R.ok().put("coupons",Arrays.asList(couponEntity));
    }

3.这样我们准备好了优惠券的调用内容,然后再member模块中调用

在member的配置类上加注解

@EnableFeignClients(basePackages="com.yxj.gulimall.member.feign")

告诉spring这里面是一个远程调用客户端,member要调用的接口

package com.ljn.gulimall.member;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/*
 * 想要远程调用的步骤:
 * 1 引入openfeign
 * 2 编写一个接口,接口告诉springcloud这个接口需要调用远程服务
 * 	2.1 在接口里声明@FeignClient("gulimall-coupon")他是一个远程调用客户端且要调用coupon服务
 * 	2.2 要调用coupon服务的/coupon/coupon/member/list方法
 * 3 开启远程调用功能 @EnableFeignClients,要指定远程调用功能放的基础包
 * */
@EnableFeignClients(basePackages="com.ljn.gulimall.member.feign")//扫描接口方法注解
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallMemberApplication {

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

}

4…那么要调用什么东西呢?就是我们刚才写的优惠券的功能,复制函数部分.

1.在member的com.ljn.gulimall.member.feign包下新建类:

// 这是一个声明式的远程调用
@FeignClient("gulimall-coupon")
public interface CouponFeignService {
    //告诉spring cloud这个接口是一个远程客户端,要调用coupon服务(nacos中找到),具体是调用coupon服务的/coupon/coupon/member/list对应的方法
    // 远程服务的url
    @RequestMapping("/coupon/coupon/member/list")//注意写全优惠券类上还有映射//注意我们这个地方不是控制层,所以这个请求映射请求的不是我们服务器上的东西,而是nacos注册中心的
    public R membercoupons();//得到一个R对象

}

2.在member的控制层写一个测试请求

@RestController
@RequestMapping("member/member")
public class MemberController {
    @Autowired
    private MemberService memberService;

    //   注入远程调用的服务
    @Autowired
    CouponFeignService couponFeignService;

    //  测试方法
    @RequestMapping("/coupons")
    public R test() {
        MemberEntity memberEntity = new MemberEntity();
        memberEntity.setNickname("张三");

        // 获取优惠券
        R membercoupons = couponFeignService.membercoupons();//假设张三去数据库查了后返回了张三的优惠券信息

        //打印会员和优惠券信息
        return R.ok().put("member", memberEntity).put("coupons", membercoupons.get("coupons"));// membercoupons.get("coupons") 获取coupons中放入的coupons
    }

5.重新启动服务刷新容器

测试:http://localhost:8000/member/member/coupons
在这里插入图片描述

5.Nacos配置中心

1、配置中心使用

用nacos作为配置中心。配置中心的意思是不在application.properties等文件中配置了,而是放到nacos配置中心公用,这样无需每台机器都改。

1.common中添加依赖 nacos配置中心
<dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
 </dependency>

2.在coupon模块中创建/src/main/resources/bootstrap.properties ,这个文件是springboot里规定的,他优先级别比application.properties高
# 改名字,对应nacos里的配置文件名
spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=localhost:8848
3.测试配合@Value注解使用获取配置
@RestController
@RequestMapping("coupon/coupon")
public class CouponController {
    @Autowired
    private CouponService couponService;

   //从application.properties中获取
    // 不要写user.name,他是环境里的变量
    @Value("${coupon.user.name}")
    private String name;
    @Value("${coupon.user.age}")
    private Integer age;

    @RequestMapping("/test")
    public R test() {
    	return R.ok().put("name", name).put("age", age);
    }

4.浏览器去nacos里的配置列表,点击+号,

data ID:gulimall-coupon.properties,添加配置
默认规则:应用名.properties

coupon.user.name=zhagnsan
coupon.user.age=100

实际生产中不能重启应用。在coupon的控制层上加@RefreshScope
动态获取配置:

  • @RefreshScope: 动态获取并刷新配置
  • @Value(“配置项的名”):获取到配置
@RefreshScope
@RestController
@RequestMapping("coupon/coupon")
public class CouponController {
    @Autowired
    private CouponService couponService;

    @Value("${coupon.user.name}")//从application.properties中获取//不要写user.name,他是环境里的变量
    private String name;
    @Value("${coupon.user.age}")
    private Integer age;
    
    @RequestMapping("/test")
    public R test(){
        return R.ok().put("name",name).put("age",age);
    }

nacos的配置内容优先于项目本地的配置内容。

2、Nacos配置中心命名空间与配置分组

在nacos浏览器中还可以配置:

  • 命名空间:用作配置隔离。(一般每个微服务一个命名空间)

    • 默认public。默认新增的配置都在public空间下
    • 开发、测试、开发可以用命名空间分割。
    • properties每个空间有一份。也可以为每个微服务配置一个命名空间,微服务互相隔离
    • 在bootstrap.properties里配置:

    可以选择对应的命名空间 # 写上对应环境的命名空间ID
    spring.cloud.nacos.config.namespace=b176a68a-6800-4648-833b-be10be8bab00

  • 配置集:一组相关或不相关配置项的集合

  • 配置集ID:类似于配置文件名,即Data ID

  • 配置分组:默认所有的配置集都属于DEFAULT_GROUP。双十一,618的优惠策略改分组即可

更改配置分组
spring.cloud.nacos.config.group=DEFAULT_GROUP(选择需要使用的组)

项目中的使用:每个微服务创建自己的命名空间,使用配置分组区分环境,dev,test,prod。

3、同时加载多个配置集

我们要把原来application.yml里的内容都分文件抽离出去。我们在nacos里创建好后,在coupons里指定要导入的配置即可。

比如抽取:datasource.yml、mybatis.yml、other.yml。

在bootstrap.properties中加载配置

# 改名字,对应nacos里的配置文件名
spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=localhost:8848

# 可以选择对应的命名空间 # 写上对应环境的命名空间ID
spring.cloud.nacos.config.namespace=79d5ebaa-bd56-446e-97e1-ba06cd7e51fa
# 更改配置分组
spring.cloud.nacos.config.group=dev


spring.cloud.nacos.config.extension-configs[0].data-id=datasource.yml
spring.cloud.nacos.config.extension-configs[0].group=dev
spring.cloud.nacos.config.extension-configs[0].refresh=true

spring.cloud.nacos.config.extension-configs[1].data-id=mybatis.yml
spring.cloud.nacos.config.extension-configs[1].group=dev
spring.cloud.nacos.config.extension-configs[1].refresh=true

spring.cloud.nacos.config.extension-configs[2].data-id=other.yml
spring.cloud.nacos.config.extension-configs[2].group=dev
spring.cloud.nacos.config.extension-configs[2].refresh=true

6.Gateway网关

1、简介

网关是请求流量的入口,常用功能包括路由转发权限校验限流控制等。springcloud gateway取代了zuul网关。

2、核心概念

  • Route(路由):发一个请求给网关,网关要将请求路由到指定的服务。路由有id,目的地uri,断言的集合,匹配了断言就能到达指定位置
  • Predicate(断言):就是java里的断言函数,匹配请求里的任何信息,包括请求头等。根据请求头路由哪个服务
  • Filter(过滤):过滤器请求和响应都可以被修改。

流程:
在这里插入图片描述
客户端发请求给服务端。中间有网关。先交给映射器,如果能处理就交给handler处理,然后交给一系列filer,然后给指定的服务,再返回回来给客户端。

断言工厂:
在这里插入图片描述

3、网关使用

1.创建模块gulimall-gateway
2.在pom中引入
 <!--        依赖common-->
        <dependency>
            <groupId>com.ljn.gulimall</groupId>
            <artifactId>gulimall-commom</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
3.开启注册服务发现@EnableDiscoveryClient

在主启动类中添加注解@EnableDiscoveryClient

@EnableDiscoveryClient
@SpringBootApplication
public class GulimallGatewayApplication {

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

}

4.相关配置

去nacos里创建命名空间gateway(项目与项目用命名空间隔离),然后在命名空间里创建文件guilmall-gateway.yml

  1. applicaion.properties
spring.application.name=gulimall-gateway
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
server.port=88
  1. bootstrap.properties 填写nacos配置中心地址
spring.application.name=gulimall-gateway
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=0769376d-1b8c-49ce-a9aa-b814667204ec
  1. application.yml,根据条件转发到uri等
spring:
  cloud:
    gateway:
      routes:
        - id: baidu_route
          uri: http://www.baidu.com
          predicates:
            - Query=url,baidu

        - id: test_route
          uri: http://www.qq.com
          predicates:
            - Query=url,qq
5.测试
  • 测试 localhost:88?url=baidu # 跳到百度页面
  • 测试 localhost:88?url=baidu # 跳到qq页面

八、前端基础

1.前端技术栈对比

在这里插入图片描述

1、ES6

let&&const
var{}之外也起作用
let在{}之外不起作用
var多次声明同一变量不会报错,let多次声明会报错,只能声明一次。
var 会变量提升(打印和定义可以顺序反)。let 不存在变量提升(顺序不能反)
const声明之后不允许改变
解构表达式
支持let arr = [1,2,3];     let [a,b,c] = arr;这种语法
支持对象解析:const { name: abc, age, language } = person; 冒号代表改名
字符串函数
支持一个字符串为多行
占位符功能 ${}
函数优化
原来想要函数默认值得这么写b = b || 1; 现在可以直接写了function add2(a, b = 1) {
函数不定参数function fun(...values) {
支持箭头函数(lambda表达式),还支持使用{}结构传入对象的成员

    //箭头函数+解构
        var hello2 = ({name}) => console.log("hello," +name);
        hello2(person);
对象优化
可以获取map的键值对等Object.keys()、values、entries
Object.assgn(target,source1,source2) 合并
const person2 = { age, name } //声明对象简写
…代表取出该对象所有属性拷贝到当前对象。let someone = { …p1 }
map和reduce
//数组中新增了map和reduce方法。

        //map():接收一个函数,将原数组中的所有元素用这个函数处理后放入新数组返回。
         let arr = ['1', '20', '-5', '3'];
         arr = arr.map(item=> item*2);

		 //reduce() 为数组中的每一个元素依次执行回调函数,不包括数组中被删除或从未被赋值的元素,
		 //[2, 40, -10, 6]
        //arr.reduce(callback,[initialValue])
		 1、previousValue (上一次调用回调返回的值,或者是提供的初始值(initialValue))
    	 2、currentValue (数组中当前被处理的元素)
    	 3、index (当前元素在数组中的索引)
    	 4、array (调用 reduce 的数组

		 let result = arr.reduce((a,b)=>{
		            console.log("上一次处理后:"+a);
		            console.log("当前正在处理:"+b);
		            return a + b;
		        },100);

    	
promise
corse_score_10.json 得分
{
    "id": 100,
    "score": 90
}

user.json 用户
{
    "id": 1,
    "name": "zhangsan",
    "password": "123456"
}


user_corse_1.json 课程
{
    "id": 10,
    "name": "chinese"
}

以前嵌套ajax的时候很繁琐。

解决方案:
把Ajax封装到Promise中,赋值给let p
在Ajax中成功使用resolve(data),交给then处理,
失败使用reject(err),交给catch处理p.then().catch()

	//1、Promise可以封装异步操作
         let p = new Promise((resolve, reject) => { //传入成功解析,失败拒绝
             //1、异步操作
             $.ajax({
                 url: "mock/user.json",
                 success: function (data) {
                     console.log("查询用户成功:", data)
                     resolve(data);
                 },
                 error: function (err) {
                     reject(err);
                 }
             });
         });


	   p.then((obj) => { //成功以后做什么
	             return new Promise((resolve, reject) => {
	                 $.ajax({
	                     url: `mock/user_corse_${obj.id}.json`,
	                     success: function (data) {
	                         console.log("查询用户课程成功:", data)
	                         resolve(data);
	                     },
	                     error: function (err) {
	                         reject(err)
	                     }
	                 });
	             })
	         }).then((data) => { //成功以后干什么
	             console.log("上一步的结果", data)
	             $.ajax({
	                 url: `mock/corse_score_${data.id}.json`,
	                 success: function (data) {
	                     console.log("查询课程得分成功:", data)
	                 },
	                 error: function (err) {
	                 }
	             });
	         })
	//自己定义一个方法整合一下
	function get(url, data) { 
	            return new Promise((resolve, reject) => {
	                $.ajax({
	                    url: url,
	                    data: data,
	                    success: function (data) {
	                        resolve(data);
	                    },
	                    error: function (err) {
	                        reject(err)
	                    }
	                })
	            });
	        }

	get("mock/user.json")
	            .then((data) => {
	                console.log("用户查询成功~~~:", data)
	                return get(`mock/user_corse_${data.id}.json`);
	            })
	            .then((data) => {
	                console.log("课程查询成功~~~:", data)
	                return get(`mock/corse_score_${data.id}.json`);
	            })
	            .then((data)=>{
	                console.log("课程成绩查询成功~~~:", data)
	            })
	            .catch((err)=>{ //失败的话catch
	                console.log("出现异常",err)
	            });


模块化
模块化就是把代码进行拆分,方便重复利用。类似于java中的导包,
而JS换了个概念,是导模块。

模块功能主要有两个命令构成 export 和import

export用于规定模块的对外接口
import用于导入其他模块提供的功能

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
P44题: 1. 假设TCP的最大段大小为1500字节,以太网的MTU为1500字节,IP首部长度为20字节,TCP首部长度为20字节。则TCP数据报的最大有效载荷为1500-20-20=1460字节。因此,每个TCP段最多能够携带1460字节的有效数据。 2. 当TCP在发送端接收到应用层的数据时,它将数据分割成由最大段大小限制的一系列TCP段。在接收端,TCP再将段组装成接收端应用程序能够使用的数据。因此,TCP在发送端对应用层数据进行分割,而在接收端对TCP段进行组装。 3. 在TCP的慢启动过程中,拥塞窗口cwnd的值从1开始,每经过一个往返时间RTT,cwnd的大小翻倍。因此,第一个RTT结束时,cwnd的值为2,第二个RTT结束时,cwnd的值为4,依此类推。在cwnd的值达到一个阈值ssthresh之前,TCP的拥塞控制采用慢启动算法。当cwnd的值达到ssthresh时,TCP的拥塞控制采用拥塞避免算法。 4. 在TCP的拥塞控制中,拥塞窗口cwnd的大小是根据网络的拥塞程度来进行调整的。当网络中出现拥塞时,TCP会减小cwnd的大小以减轻网络的拥塞情况。在TCP的快速恢复算法中,当发送端接收到3个重复的ACK时,它会假定网络中出现了拥塞,并将cwnd的值设置为ssthresh的一半,然后继续发送数据。这种方法比慢启动算法更加高效。 P45题: 1. IP首部长度为20字节,因此IP数据报的最大长度为2^16-1-20=65515字节,其中2^16-1是IP数据报的最大总长度。 2. 在IP分组中,标识字段和标志字段用于实现分组的重组。标识字段用于标识属于同一数据报的多个分组,标志字段用于指示分组在分组序列中的位置。例如,第一个分组的标志字段为0,中间的分组的标志字段为1,最后一个分组的标志字段为0。 3. IP分组在路由器中进行转发时,路由器需要查找路由表以确定下一跳路由器的地址。当路由器收到一个IP分组时,它首先进行CRC校验以检查分组的完整性。然后,路由器根据目的IP地址查找路由表,并将分组转发到下一跳路由器的接口。 4. 在IPv6中,每个节点都有一个本地链路地址和一个全局地址。本地链路地址用于在链路层进行通信,全局地址用于在互联网中进行通信。IPv6地址长度为128位,采用冒号十六进制表示法,例如:2001:0db8:85a3:0000:0000:8a2e:0370:7334。IPv6还引入了一种新的地址类型,即任播地址,它用于向一组节点中的任意一个节点发送数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绿箭柠檬茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值