## 1. 整体介绍 ![image-20200422211607028](images/image-20200422211607028.png) ### 1)安装vagrant ### 2)安装Centos7 ```shell $ vagrant init centos/7 A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant. ``` 执行完上面的命令后,会在用户的家目录下生成Vagrantfile文件。 ```shell $ vagrant up Bringing machine 'default' up with 'virtualbox' provider... ==> default: Box 'centos/7' could not be found. Attempting to find and install... default: Box Provider: virtualbox default: Box Version: >= 0 ==> default: Loading metadata for box 'centos/7' default: URL: https://vagrantcloud.com/centos/7 ==> default: Adding box 'centos/7' (v1905.1) for provider: virtualbox default: Downloading: https://vagrantcloud.com/centos/boxes/7/versions/1905.1/providers/virtualbox.box default: Download redirected to host: cloud.centos.org default: Progress: 0% (Rate: 6717/s, Estimated time remaining: 7:33:42) ``` 下载镜像过程比较漫长,也可以采用先用下载工具下载到本地后,然后使用“ vagrant box add ”添加,再“vagrant up”即可 ```shell #将下载的镜像添加到virtualBox中 $ vagrant box add centos/7 E:\迅雷下载\CentOS-7-x86_64-Vagrant-1905_01.VirtualBox.box ==> box: Box file was not detected as metadata. Adding it directly... ==> box: Adding box 'centos/7' (v0) for provider: box: Unpacking necessary files from: file:///E:/%D1%B8%C0%D7%CF%C2%D4%D8/CentOS-7-x86_64-Vagrant-1905_01.VirtualBox.box box: ==> box: Successfully added box 'centos/7' (v0) for 'virtualbox'! #启动 $ vagrant up Bringing machine 'default' up with 'virtualbox' provider... ==> default: Importing base box 'centos/7'... ==> default: Matching MAC address for NAT networking... ==> default: Setting the name of the VM: Administrator_default_1588497928070_24634 ==> default: Clearing any previously set network interfaces... ==> default: Preparing network interfaces based on configuration... default: Adapter 1: nat default: Adapter 2: hostonly ==> default: Forwarding ports... default: 22 (guest) => 2222 (host) (adapter 1) ==> default: Booting VM... ==> default: Waiting for machine to boot. This may take a few minutes... default: SSH address: 127.0.0.1:2222 default: SSH username: vagrant default: SSH auth method: private key default: default: Vagrant insecure key detected. Vagrant will automatically replace default: this with a newly generated keypair for better security. default: default: Inserting generated public key within guest... default: Removing insecure key from the guest if it's present... default: Key inserted! Disconnecting and reconnecting using new SSH key... ==> default: Machine booted and ready! ==> default: Checking for guest additions in VM... default: No guest additions were detected on the base box for this VM! Guest default: additions are required for forwarded ports, shared folders, host only default: networking, and more. If SSH fails on this machine, please install default: the guest additions and repackage the box to continue. default: default: This is not an error message; everything may continue to work properly, default: in which case you may ignore this message. ==> default: Configuring and enabling network interfaces... ==> default: Rsyncing folder: /cygdrive/c/Users/Administrator/ => /vagrant ``` vagrant ssh 开启SSH,并登陆到centos7 ```shell $ vagrant ssh [vagrant@localhost ~]$ ip addr 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 52:54:00:8a:fe:e6 brd ff:ff:ff:ff:ff:ff inet 10.0.2.15/24 brd 10.0.2.255 scope global noprefixroute dynamic eth0 valid_lft 86091sec preferred_lft 86091sec inet6 fe80::5054:ff:fe8a:fee6/64 scope link valid_lft forever preferred_lft forever 3: eth1: mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 08:00:27:d1:76:f6 brd ff:ff:ff:ff:ff:ff inet 192.168.56.102/24 brd 192.168.56.255 scope global noprefixroute dynamic eth1 valid_lft 892sec preferred_lft 892sec inet6 fe80::8c94:1942:ba09:2458/64 scope link noprefixroute valid_lft forever preferred_lft forever [vagrant@localhost ~]$ ``` ```shell C:\Users\Administrator>ipconfig Windows IP 配置 以太网适配器 VirtualBox Host-Only Network: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : fe80::a00c:1ffa:a39a:c8c2%16 IPv4 地址 . . . . . . . . . . . . : 192.168.56.1 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : ``` 配置网络信息,打开"Vagrantfile"文件: ``` config.vm.network "private_network", ip: "192.168.56.10" ``` 修改完成后,重启启动vagrant ``` vagrant reload ``` 检查宿主机和virtualBox之间的通信是否正常 ``` [vagrant@localhost ~]$ ping 192.168.43.43 PING 192.168.43.43 (192.168.43.43) 56(84) bytes of data. 64 bytes from 192.168.43.43: icmp_seq=1 ttl=127 time=0.533 ms 64 bytes from 192.168.43.43: icmp_seq=2 ttl=127 time=0.659 ms  --- 192.168.43.43 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 999ms rtt min/avg/max/mdev = 0.533/0.596/0.659/0.063 ms [vagrant@localhost ~]$ [vagrant@localhost ~]$ [vagrant@localhost ~]$ ping www.baidu.com PING www.a.shifen.com (112.80.248.76) 56(84) bytes of data. 64 bytes from 112.80.248.76 (112.80.248.76): icmp_seq=1 ttl=53 time=56.1 ms 64 bytes from 112.80.248.76 (112.80.248.76): icmp_seq=2 ttl=53 time=58.5 ms 64 bytes from 112.80.248.76 (112.80.248.76): icmp_seq=3 ttl=53 time=53.4 ms  ``` 开启远程登陆,修改“/etc/ssh/sshd_config” ```shell PermitRootLogin yes PasswordAuthentication yes ``` 然后重启SSHD ```shell systemctl restart sshd ``` 使用Xshell或SecureCRT进行远程连接。 ![image-20200503174735162](images/image-20200503174735162.png) ## 2. docker中安装mysql ```shell [root@hadoop-104 module]# docker pull mysql:5.7 5.7: Pulling from library/mysql 123275d6e508: Already exists 27cddf5c7140: Pull complete c17d442e14c9: Pull complete 2eb72ffed068: Pull complete d4aa125eb616: Pull complete 52560afb169c: Pull complete 68190f37a1d2: Pull complete 3fd1dc6e2990: Pull complete 85a79b83df29: Pull complete 35e0b437fe88: Pull complete 992f6a10268c: Pull complete Digest: sha256:82b72085b2fcff073a6616b84c7c3bcbb36e2d13af838cec11a9ed1d0b183f5e Status: Downloaded newer image for mysql:5.7 docker.io/library/mysql:5.7 ``` 查看镜像 ``` [root@hadoop-104 module]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE mysql 5.7 f5829c0eee9e 2 hours ago 455MB [root@hadoop-104 module]# ``` 启动mysql ```shell 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 ``` 修改配置 ```properties [root@hadoop-104 conf]# pwd /mydata/mysql/conf [root@hadoop-104 conf]# cat 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 [root@hadoop-104 conf]# [root@hadoop-104 conf]# docker restart mysql mysql [root@hadoop-104 conf]# ``` 进入容器查看配置: ```shell [root@hadoop-104 conf]# docker exec -it mysql /bin/bash root@b3a74e031bd7:/# whereis mysql mysql: /usr/bin/mysql /usr/lib/mysql /etc/mysql /usr/share/mysql root@b3a74e031bd7:/# ls /etc/mysql my.cnf root@b3a74e031bd7:/# cat /etc/mysql/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 root@b3a74e031bd7:/# ``` 设置启动docker时,即运行mysql ``` [root@hadoop-104 ~]# docker update mysql --restart=always mysql [root@hadoop-104 ~]# ``` ## 3. docker中安装redis 下载docker ```shell [root@hadoop-104 ~]# docker pull redis Using default tag: latest latest: Pulling from library/redis 123275d6e508: Already exists f2edbd6a658e: Pull complete 66960bede47c: Pull complete 79dc0b596c90: Pull complete de36df38e0b6: Pull complete 602cd484ff92: Pull complete Digest: sha256:1d0b903e3770c2c3c79961b73a53e963f4fd4b2674c2c4911472e8a054cb5728 Status: Downloaded newer image for redis:latest docker.io/library/redis:latest ``` 启动docker ```shell [root@hadoop-104 ~]# mkdir -p /mydata/redis/conf [root@hadoop-104 ~]# touch /mydata/redis/conf/redis.conf [root@hadoop-104 ~]# echo "appendonly yes" >> /mydata/redis/conf/redis.conf [root@hadoop-104 ~]# 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 ce7ae709711986e3f90c9278b284fe6f51f1c1102ba05f3692f0e934ceca1565 [root@hadoop-104 ~]# ``` 连接到docker的redis ```shell [root@hadoop-104 ~]# docker exec -it redis redis-cli 127.0.0.1:6379> set key1 v1 OK 127.0.0.1:6379> get key1 "v1" 127.0.0.1:6379> ``` 设置redis容器在docker启动的时候启动 ```shell [root@hadoop-104 ~]# docker update redis --restart=always redis [root@hadoop-104 ~]# ``` ## 4. 创建maven工程 ## 5. 执行sql脚本 gulimall_oms.sql gulimall_pms.sql gulimall_sms.sql gulimall_ums.sql gulimall_wms.sql pms_catelog.sql sys_menus.sql ## 6. clone 人人开源 ![1587609877028](images/1587609877028.png) 克隆到本地: ```shell git clone https://gitee.com/renrenio/renren-fast-vue.git git clone https://gitee.com/renrenio/renren-fast.git ``` 将拷贝下来的“renren-fast”删除“.git”后,拷贝到“gulimall”工程根目录下,然后将它作为gulimall的一个module 创建“gulimall_admin”的数据库,然后执行“renren-fast/db/mysql.sql”中的SQl脚本 修改“application-dev.yml”文件,默认为dev环境,修改连接mysql的url和用户名密码 ```yaml spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.137.14:3306/gulimall_admin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: root ``` 启动“gulimall_admin”,然后访问“” ![1587616296253](images/1587616296253.png) 安装node.js,并且安装仓库 ``` npm config set registry http://registry.npm.taobao.org/ ``` ```shell PS D:\tmp\renren-fast-vue> npm config set registry http://registry.npm.taobao.org/ PS D:\tmp\renren-fast-vue> npm install npm WARN ajv-keywords@1.5.1 requires a peer of ajv@>=4.10.0 but none is installed. You must install peer dependencies yourself. npm WARN sass-loader@6.0.6 requires a peer of node-sass@^4.0.0 but none is installed. You must install peer dependencies yourself. npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@1.2.9 (node_modules\fsevents): npm WARN notsup SKIPPING OPTIONAL DEPENDENCY: Unsupported platform for fsevents@1.2.9: wanted {"os":"darwin","arch":"any"} (current: {"os":"win32","arch":"x64"}) up to date in 17.227s PS D:\tmp\renren-fast-vue> ``` ```shell PS D:\tmp\renren-fast-vue> npm run dev > renren-fast-vue@1.2.2 dev D:\tmp\renren-fast-vue > webpack-dev-server --inline --progress --config build/webpack.dev.conf.js 10% building modules 5/10 modules 5 active ...-0!D:\tmp\renren-fast-vue\src\main.js(node:19864) Warning: Accessing non-existent property 'cat' of module exports inside circular dependency (Use `node --trace-warnings ...` to show where the warning was created) (node:19864) Warning: Accessing non-existent property 'cd' of module exports inside circular dependency (node:19864) Warning: Accessing non-existent property 'chmod' of module exports inside circular dependency (node:19864) Warning: Accessing non-existent property 'cp' of module exports inside circular dependency (node:19864) Warning: Accessing non-existent property 'dirs' of module exports inside circular dependency (node:19864) Warning: Accessing non-existent property 'pushd' of module exports inside circular dependency (node:19864) Warning: Accessing non-existent property 'popd' of module exports inside circular dependency (node:19864) Warning: Accessing non-existent property 'echo' of module exports inside circular dependency (node:19864) Warning: Accessing non-existent property 'tempdir' of module exports inside circular dependency (node:19864) Warning: Accessing non-existent property 'pwd' of module exports inside circular dependency ``` 常见问题1:“Module build failed: Error: Cannot find module 'node-sass” 运行过程中,出现“Module build failed: Error: Cannot find module 'node-sass'报错问题”,解决方法 > > >用npm install -g cnpm --registry=https://registry.npm.taobao.org ,从淘宝镜像那下载,然后cnpm下载成功。 > >最后输入cnpm install node-sass --save。npm run dev终于能跑起来了!!! >———————————————— >版权声明:本文为CSDN博主「夕阳下美了剪影」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 >原文链接:https://blog.csdn.net/qq_38401285/article/details/86483278 常见问题2:cnpm - 解决 " cnpm : 无法加载文件 C:\Users\93457\AppData\Roaming\npm\cnpm.ps1,因为在此系统上禁止运行脚本。有关详细信息 。。。 " 所有问题的根源都在“node_modules”,npm install之前,应该将这个文件夹删除,然后再进行安装和运行。 再次运行npm run dev恢复正常: ![1587637858665](images/1587637858665.png) ## 7. clone renren-generator ### clone https://gitee.com/renrenio/renren-generator.git 然后将该项目放置到“gulimall”的跟路径下,然后添加该Module,并且提交到github上 ### 修改配置 renren-generator/src/main/resources/generator.properties ```properties #代码生成器,配置信息 mainPath=com.bigdata #包名 package=com.bigdata.gulimall moduleName=product #作者 author=cosmoswong #Email email=cosmoswong@sina.com #表前缀(类名不会包含表前缀) tablePrefix=pms_ ``` ### 运行“renren-generator” 访问: com.baomidou mybatis-plus-boot-starter 3.2.0 ``` 2)、配置 1、配置数据源; 1)、导入数据库的驱动。https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-versions.html 2)、在application.yml配置数据源相关信息 ```yml spring: datasource: username: root password: root url: jdbc:mysql://#:3306/gulimall_pms driver-class-name: com.mysql.cj.jdbc.Driver ``` 2、配置MyBatis-Plus; 1)、使用`@MapperScan` 2)、告诉MyBatis-Plus,sql映射文件位置 ```yaml mybatis-plus: mapper-locations: classpath:/mapper/**/*.xml global-config: db-config: #主键自增 id-type: auto ``` ## 8. 微服务注册中心 要注意nacos集群所在的server,一定要关闭防火墙,否则容易出现各种问题。 搭建nacos集群,然后分别启动各个微服务,将它们注册到Nacos中。 ```yaml application: name: gulimall-coupon cloud: nacos: discovery: server-addr: 192.168.137.14 ``` 查看注册情况: ![1587694451601](images/1587694451601.png) ## 9. 使用openfen 1)、引入open-feign ```xml org.springframework.cloud spring-cloud-starter-openfeign ``` 2)、编写一个接口,告诉SpringCLoud这个接口需要调用远程服务 修改“io.niceseason.gulimall.coupon.controller.CouponController”,添加以下controller方法: ```java @RequestMapping("/member/list") public R memberCoupons(){ CouponEntity couponEntity = new CouponEntity(); couponEntity.setCouponName("discount 20%"); return R.ok().put("coupons",Arrays.asList(couponEntity)); } ``` 新建“io.niceseason.gulimall.member.feign.CouponFeignService”接口 ```java @FeignClient("gulimall_coupon") public interface CouponFeignService { @RequestMapping("/coupon/coupon/member/list") public R memberCoupons(); } ``` 修改“io.niceseason.gulimall.member.GulimallMemberApplication”类,添加上"@EnableFeignClients": ```java @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackages = "io.niceseason.gulimall.member.feign") public class GulimallMemberApplication { public static void main(String[] args) { SpringApplication.run(GulimallMemberApplication.class, args); } } ``` ​ 声明接口的每一个方法都是调用哪个远程服务的那个请求 3)、开启远程调用功能 io.niceseason.gulimall.member.controller.MemberController ```java @RequestMapping("/coupons") public R test(){ MemberEntity memberEntity=new MemberEntity(); memberEntity.setNickname("zhangsan"); R memberCoupons = couponFeignService.memberCoupons(); return memberCoupons.put("member",memberEntity).put("coupons",memberCoupons.get("coupons")); } ``` (4)、访问 ![1587701348764](images/1587701348764.png) 停止“gulimall-coupon”服务,能够看到注册中心显示该服务的健康值为0: ![1587701521184](images/1587701521184.png) 再次访问:http://localhost:8000/member/member/coupons ![1587701587456](images/1587701587456.png) 启动“gulimall-coupon”服务,再次访问,又恢复了正常。 ## 10. 配置中心 ### 1)修改“gulimall-coupon”模块 添加pom依赖: ```xml com.alibaba.cloud spring-cloud-starter-alibaba-nacos-config ``` 创建bootstrap.properties文件,该配置文件会优先于“application.yml”加载。 ```properties spring.application.name=gulimall-coupon spring.cloud.nacos.config.server-addr=192.168.137.14:8848 ``` ### 2)传统方式 为了详细说明config的使用方法,先来看原始的方式 创建“application.properties”配置文件,添加如下配置内容: ```properties coupon.user.name="zhangsan" coupon.user.age=30 ``` 修改“io.niceseason.gulimall.coupon.controller.CouponController”文件,添加如下内容: ```java @Value("${coupon.user.name}") private String name; @Value("${coupon.user.age}") private Integer age; @RequestMapping("/test") public R getConfigInfo(){ return R.ok().put("name",name).put("age",age); } ``` 启动“gulimall-coupon”服务: 访问:http://localhost:7000/coupon/coupon/test> ![1587716583668](images/1587716583668.png) 这样做存在的一个问题,如果频繁的修改application.properties,在需要频繁重新打包部署。下面我们将采用Nacos的配置中心来解决这个问题。 ### 3)nacos config 1、在Nacos注册中心中,点击“配置列表”,添加配置规则: ![1587716911435](images/1587716911435.png) DataID:gulimall-coupon 配置格式:properties 文件的命名规则为:${spring.application.name}-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension} ${spring.application.name}:为微服务名 ${spring.profiles.active}:指明是哪种环境下的配置,如dev、test或info ${spring.cloud.nacos.config.file-extension}:配置文件的扩展名,可以为properties、yml等 2、查看配置: ![1587717125580](images/1587717125580.png) 3、修改“io.niceseason.gulimall.coupon.controller.CouponController”类,添加“@RefreshScope”注解 ```java @RestController @RequestMapping("coupon/coupon") @RefreshScope public class CouponController { ``` 这样都会动态的从配置中心读取配置. 4、访问: ![1587717485283](images/1587717485283.png) 能够看到读取到了nacos 中的最新的配置信息,并且在指明了相同的配置信息时,配置中心中设置的值优先于本地配置。 ### 4)Nacos支持三种配置加载方方案 Nacos支持“Namespace+group+data ID”的配置解决方案。 详情见: ##### Namespace方案 通过命名空间实现环境区分 下面是配置实例: 1、创建命名空间: “命名空间”—>“创建命名空间”: ![1587718802109](images/1587718802109.png) 创建三个命名空间,分别为dev,test和prop 2、回到配置列表中,能够看到所创建的三个命名空间 ![1587718889316](images/1587718889316.png) 下面我们需要在dev命名空间下,创建“gulimall-coupon.properties”配置规则: ![1587719108947](images/1587719108947.png) 3、访问: ![1587721184218](images/1587721184218.png) 并没有使用我们在dev命名空间下所配置的规则,而是使用的是public命名空间下所配置的规则,这是怎么回事呢? 查看“gulimall-coupon”服务的启动日志: ```verilog 2020-04-24 16:37:24.158 WARN 32792 --- [ main] c.a.c.n.c.NacosPropertySourceBuilder : Ignore the empty nacos configuration and get it based on dataId[gulimall-coupon] & group[DEFAULT_GROUP] 2020-04-24 16:37:24.163 INFO 32792 --- [ main] c.a.nacos.client.config.utils.JVMUtil : isMultiInstance:false 2020-04-24 16:37:24.169 INFO 32792 --- [ main] b.c.PropertySourceBootstrapConfiguration : Located property source: [BootstrapPropertySource {name='bootstrapProperties-gulimall-coupon.properties,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-gulimall-coupon,DEFAULT_GROUP'}] ``` **"gulimall-coupon.properties"**,默认就是public命名空间中的内容中所配置的规则。 4、指定命名空间 如果想要使得我们自定义的命名空间生效,需要在“bootstrap.properties”文件中,指定使用哪个命名空间: ```properties spring.cloud.nacos.config.namespace=a2c83f0b-e0a8-40fb-9b26-1e9d61be7d6d ``` 这个命名空间ID来源于我们在第一步所创建的命名空间 ![1587718802109](images/1587718802109.png) 5、重启“gulimall-coupon”,再次访问:http://localhost:7000/coupon/coupon/test ![1587720311349](images/1587720311349.png) 但是这种命名空间的粒度还是不够细化,对此我们可以为项目的每个微服务module创建一个命名空间。 6、为所有微服务创建命名空间 ![1587720714101](images/1587720714101.png) 7、回到配置列表选项卡,克隆pulic的配置规则到coupon命名空间下 ![1587720883244](images/1587720883244.png) 切换到coupon命名空间下,查看所克隆的规则: ![1587720963699](images/1587720963699.png) 8、修改“gulimall-coupon”下的bootstrap.properties文件,添加如下配置信息 ```properties spring.cloud.nacos.config.namespace=7905c915-64ad-4066-8ea9-ef63918e5f79 ``` 这里指明的是,读取时使用coupon命名空间下的配置。 9、重启“gulimall-coupon”,访问: ![1587721184218](images/1587721184218.png) ##### DataID方案 通过指定spring.profile.active和配置文件的DataID,来使不同环境下读取不同的配置,读取配置时,使用的是默认命名空间public,默认分组(default_group)下的DataID。 默认情况,Namespace=public,Group=DEFAULT GROUP,默认Cluster是DEFAULT 通过制定`spring.profiles.active=dev`可以制定`xxx-dev.properties`的配置文件 ##### Group方案 通过Group实现环境区分 实例:通过使用不同的组,来读取不同的配置,还是以上面的gulimall-coupon微服务为例 1、新建“gulimall-coupon.properties”,将它置于“tmp”组下 ![1587721616021](images/1587721616021.png) 2、修改“bootstrap.properties”配置,添加如下的配置 ```properties spring.cloud.nacos.config.group=tmp ``` 3、重启“gulimall-coupon”,访问: ![1587721844449](images/1587721844449.png) ### 5)同时加载多个配置集 当微服务数量很庞大时,将所有配置都书写到一个配置文件中,显然不是太合适。对此我们可以将配置按照功能的不同,拆分为不同的配置文件。 如下面的配置文件: ```yaml server: port: 7000 spring: datasource: #MySQL配置 driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.137.14:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: root application: name: gulimall-coupon cloud: nacos: discovery: server-addr: 192.168.137.14:8848 mybatis-plus: global-config: db-config: id-type: auto mapper-locations: classpath:/mapper/**/*.xml ``` 我们可以将, 数据源有关的配置写到一个配置文件中: ```yaml spring: datasource: #MySQL配置 driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.137.14:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: root ``` 和框架有关的写到另外一个配置文件中: ```yaml mybatis-plus: global-config: db-config: id-type: auto mapper-locations: classpath:/mapper/**/*.xml ``` 也可以将上面的这些配置交给nacos来进行管理。 实例:将“gulimall-coupon”的“application.yml”文件拆分为多个配置,并放置到nacos配置中心 1、创建“datasource.yml”,用于存储和数据源有关的配置 ```yml spring: datasource: #MySQL配置 driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.137.14:3306/gulimall_sms?useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: root ``` 在coupon命名空间中,创建“datasource.yml”配置 ![1587722798375](images/1587722798375.png) 2、将和mybatis相关的配置,放置到“mybatis.yml”中 ```yaml mybatis-plus: global-config: db-config: id-type: auto mapper-locations: classpath:/mapper/**/*.xml ``` ![1587722710432](images/1587722710432.png) 3、创建“other.yml”配置,保存其他的配置信息 ```yaml server: port: 7000 spring: application: name: gulimall-coupon cloud: nacos: discovery: server-addr: 192.168.137.14:8848 ``` ![1587722998265](images/1587722998265.png) 现在“mybatis.yml”、“datasource.yml”和“other.yml”共同构成了微服务的配置。 4、修改“gulimall-coupon”的“bootstrap.properties”文件,加载“mybatis.yml”、“datasource.yml”和“other.yml”配置 ```properties spring.cloud.nacos.config.extension-configs[0].data-id=mybatis.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=datasource.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 ``` "spring.cloud.nacos.config.ext-config"已经被废弃,建议使用“spring.cloud.nacos.config.extension-configs”,根据自己的版本选择配置。 5、注释“application.yml”文件中的所有配置 6、重启“gulimall-coupon”服务,然后访问: ![1587724212905](images/1587724212905.png) 7、访问:,查看是否能够正常的访问数据库 ![1587724350548](images/1587724350548.png) 小结: 1)、微服务任何配置信息,任何配置文件都可以放在配置中心; 2)、只需要在bootstrap.properties中,说明加载配置中心的哪些配置文件即可; 3)、@Value, @ConfigurationProperties。都可以用来获取配置中心中所配置的信息; 4)、配置中心有的优先使用配置中心中的,没有则使用本地的配置。 ## 11. 网关 ### 1、注册“gulimall-gateway”到Nacos #### 1)创建“gulimall-gateway” SpringCloud gateway #### 2)添加“gulimall-common”依赖和“spring-cloud-starter-gateway”依赖 ```xml io.niceseason.gulimall gulimall-common 1.0-SNAPSHOT org.springframework.cloud spring-cloud-starter-gateway ``` #### 3)“io.niceseason.gulimall.gulimallgateway.GulimallGatewayApplication”类上加上“@EnableDiscoveryClient”注解 #### 4)在Nacos中创建“gateway”命名空间,同时在该命名空间中创建“gulimall-gateway.yml” ![1587729576178](images/1587729576178.png) #### 5)创建“bootstrap.properties”文件,添加如下配置,指明配置中心地址和所属命名空间 ```properties spring.application.name=gulimall-gateway spring.cloud.nacos.config.server-addr=192.168.137.14:8848 spring.cloud.nacos.config.namespace=1c82552e-1af0-4ced-9a48-26f19c2d315f ``` #### 6)创建“application.properties”文件,指定服务名和注册中心地址 ```properties spring.application.name=gulimall-gateway spring.cloud.nacos.discovery.server-addr=192.168.137.14:8848 server.port=88 ``` #### 7)启动“gulimall-gateway” 启动报错: ```verilog Description: Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class ``` 解决方法:在“io.niceseason.gulimall.gulimallgateway.GulimallGatewayApplication”中排除和数据源相关的配置 ``` java @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) ``` 重新启动 访问:,查看到该服务已经注册到了Nacos中 ![1587730035866](images/1587730035866.png) ### 2、案例 现在想要实现针对于“http://localhost:88/hello?url=baidu”,转发到“https://www.baidu.com”,针对于“http://localhost:88/hello?url=qq”的请求,转发到“https://www.qq.com/” #### 1)创建“application.yml” ```yaml spring: cloud: gateway: routes: - id: baidu_route uri: https://www.baidu.com predicates: - Query=url, baidu - id: qq_route uri: https://www.qq.com/ predicates: - Query=url, qq ``` #### 2)启动“gulimall-gateway” #### 3)测试 访问: 访问: [Gateway官方文档](https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.2.RELEASE/reference/html/#gateway-request-predicates-factories) ## 12. Vue 安装vue ``` # 最新稳定版 $ npm install vue ``` ### 1、vue声明式渲染 ```javascript let vm = new Vue({ el: "#app",//绑定元素 data: { //封装数据 name: "张三", num: 1 }, methods:{ //封装方法 cancle(){ this.num -- ; }, hello(){ return "1" } } }); ``` ### 2、双向绑定,模型变化,视图变化。反之亦然 双向绑定使用v-model ```html ``` ```javascript

{{name}} ,非常帅,有{{num}}个人为他点赞{{hello()}}

``` 1587746815353 ### 3、事件处理 v-xx:指令 1、创建vue实例,关联页面的模板,将自己的数据(data)渲染到关联的模板,响应式的 2、指令来简化对dom的一些操作。 3、声明方法来做更复杂的操作。methods里面可以封装方法。 v-on是按钮的单击事件: ``` ``` 在VUE中el,data和vue的作用: - el:用来绑定数据; - data:用来封装数据; - methods:用来封装方法,并且能够封装多个方法,如何上面封装了cancell和hello方法。 安装“Vue 2 Snippets”,用来做代码提示 ![1587747283279](images/1587747283279.png) 为了方便的在浏览器上调试VUE程序,需要安装“[vue-devtools](https://github.com/vuejs/vue-devtools)”,编译后安装到chrome中即可。 详细的使用方法见:[Vue调试神器vue-devtools安装](https://www.jianshu.com/p/63f09651724c) “v-html”不会对于HTML标签进行转义,而是直接在浏览器上显示data所设置的内容;而“ v-text”会对html标签进行转义 ```javascript
{{msg}} {{1+1}} {{hello()}}

``` 运行结果: ![1587748494597](images/1587748494597.png) {{msg}} :称为差值表达式,它必须要写在Html表达式,可以完成数学运算和方法调用 ### 4、v-bind :单向绑定 给html标签的属性绑定 ```javascript
gogogo 你好
``` 上面所完成的任务就是给a标签绑定一个超链接。并且当“isActive”和“hasError”都是true的时候,将属性动态的绑定到,则绑定该“active”和 "text-danger"class。这样可以动态的调整属性的存在。 而且如果想要实现修改vm的"color1"和“size”, span元素的style也能够随之变化,则可以写作v-bind:style,也可以省略v-bind。 ### 5、v-model双向绑定 ```html Document
精通的语言: java
PHP
Python
选中了 {{language.join(",")}}
``` 上面完成的功能就是通过“v-model”为输入框绑定多个值,能够实现选中的值,在data的language也在不断的发生着变化, ![image-20200425090955705](images/image-20200425090955705.png) 如果在控制台上指定vm.language=["Java","PHP"],则data值也会跟着变化。 ![image-20200425091736505](images/image-20200425091736505.png) 通过“v-model”实现了页面发生了变化,则数据也发生变化,数据发生变化,则页面也发生变化,这样就实现了双向绑定。 数组的连接操作: 选中了 {{language.join(",")}} ### 6、v-on为按钮绑定事件 ```javascript ``` 上面是为两个按钮绑定了单击事件,其中一个对于num进行自增,另外一个自减。 v-on:click也可以写作@click 事件的冒泡: ```html
大div
小div
去百度
``` 上面的这两个嵌套div中,如果点击了内层的div,则外层的div也会被触发;这种问题可以事件修饰符来完成: ```html
大div
小div
去百度
``` 关于事件修饰符: ![image-20200425094010008](images/image-20200425094010008.png) 按键修饰符: ![image-20200425094247167](images/image-20200425094247167.png) ![image-20200425100629676](images/image-20200425100629676.png) ### 7、v-for遍历循环 ```html Document
  • 当前索引:{{index}} ==> {{user.name}} ==> {{user.gender}} ==>{{user.age}}
    对象信息: {{k}}=={{v}}=={{i}};
``` 4、遍历的时候都加上:key来区分不同数据,提高vue渲染效率 ### 过滤器 ```html Document
  • {{user.id}} ==> {{user.name}} ==> {{user.gender == 1?"男":"女"}} ==> {{user.gender | genderFilter}} ==> {{user.gender | gFilter}}
``` ### 组件化 ```html Document
``` ![image-20200425110048496](images/image-20200425110048496.png) ### 生命周期钩子函数 ```html Document
{{num}}

{{name}},有{{num}}个人点赞

``` ## 13. element ui 官网: https://element.eleme.cn/#/zh-CN/component/installation 安装 ```shell npm i element-ui -S ``` ### 在 main.js 中写入以下内容: ``` import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css'; Vue.use(ElementUI); ``` ## 14. 递归树形结构获取数据 在注册中心中“product”命名空间中,创建“gulimall-product.yml”配置文件: ![image-20200425153735737](images/image-20200425153735737.png) 将“application.yml”内容拷贝到该配置文件中 ```yaml server: port: 10000 spring: datasource: #MySQL配置 driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.137.14:3306/gulimall_pms?useUnicode=true&characterEncoding=UTF-8&useSSL=false username: root password: root application: name: gulimall-product cloud: nacos: discovery: server-addr: 192.168.137.14:8848 mybatis-plus: global-config: db-config: id-type: auto mapper-locations: classpath:/mapper/**/*.xml ``` 在本地创建“bootstrap.properties”文件,指明配置中心的位置和使用到的配置文件: ```properties spring.application.name=gulimall-product spring.cloud.nacos.config.server-addr=192.168.137.14:8848 spring.cloud.nacos.config.namespace=3c50ffaa-010b-4b59-9372-902e35059232 spring.cloud.nacos.config.extension-configs[0].data-id=gulimall-product.yml spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP spring.cloud.nacos.config.extension-configs[0].refresh=true ``` 然后启动gulimall-product,查看到该服务已经出现在了nacos的注册中心中了 修改“io.niceseason.gulimall.product.service.CategoryService”类,添加如下代码: ```java /** * 列表 */ @RequestMapping("/list/tree") public List list(){ List categoryEntities = categoryService.listWithTree(); return categoryEntities; } ``` 测试:http://localhost:10000/product/category/list/tree ![image-20200425154348716](images/image-20200425154348716.png) 如何区别是哪种分类级别? 答:可以通过分类的parent_cid来进行判断,如果是一级分类,其值为0. ```java /** * 列表 */ @RequestMapping("/list/tree") public List list(){ List categoryEntities = categoryService.listWithTree(); //找到所有的一级分类 List level1Menus = categoryEntities.stream() .filter(item -> item.getParentCid() == 0) .map(menu->{ menu.setChildCategoryEntity(getChildrens(menu,categoryEntities)); return menu; }) .sorted((menu1, menu2) -> { return (menu1.getSort() ==null ? 0:menu1.getSort())- (menu2.getSort()==null?0:menu2.getSort()); }) .collect(Collectors.toList()); return level1Menus; } public List getChildrens(CategoryEntity root,List all){ List childrens = all.stream().filter(item -> { return item.getParentCid() == root.getCatId(); }).map(item -> { item.setChildCategoryEntity(getChildrens(item, all)); return item; }).sorted((menu1, menu2) -> { return (menu1.getSort() ==null ? 0:menu1.getSort())- (menu2.getSort()==null?0:menu2.getSort()); }).collect(Collectors.toList()); return childrens; } ``` 下面是得到的部分JSON数据 ```json [ { "catId": 1, "name": "图书、音像、电子书刊", "parentCid": 0, "catLevel": 1, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [ { "catId": 22, "name": "电子书刊", "parentCid": 1, "catLevel": 2, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [ { "catId": 165, "name": "电子书", "parentCid": 22, "catLevel": 3, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [] }, { "catId": 166, "name": "网络原创", "parentCid": 22, "catLevel": 3, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [] }, { "catId": 167, "name": "数字杂志", "parentCid": 22, "catLevel": 3, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [] }, { "catId": 168, "name": "多媒体图书", "parentCid": 22, "catLevel": 3, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [] } ] }, { "catId": 23, "name": "音像", "parentCid": 1, "catLevel": 2, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [ { "catId": 169, "name": "音乐", "parentCid": 23, "catLevel": 3, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [] }, { "catId": 170, "name": "影视", "parentCid": 23, "catLevel": 3, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [] }, { "catId": 171, "name": "教育音像", "parentCid": 23, "catLevel": 3, "showStatus": 1, "sort": 0, "icon": null, "productUnit": null, "productCount": 0, "childCategoryEntity": [] } ] }, { ``` 启动后端项目renren-fast 启动前端项目renren-fast-vue: ``` npm run dev ``` 访问: http://localhost:8001/#/login 创建一级菜单: ![image-20200425164019287](images/image-20200425164019287.png) 创建完成后,在后台的管理系统中会创建一条记录: ![image-20200425164201813](images/image-20200425164201813.png) 然后创建子菜单: ![image-20200425164509143](images/image-20200425164509143.png) 创建renren-fast-vue\src\views\modules\product目录,子所以是这样来创建,是因为product/category,对应于product-category 在该目录下,新建“category.vue”文件: ```html " ], "description": "生成VUE模板" }, "http-get请求": { "prefix": "httpget", "body": [ "this.\\$http({", "url: this.\\$http.adornUrl(''),", "method: 'get',", "params: this.\\$http.adornParams({})", "}).then(({ data }) => {", "})" ], "description": "httpGET请求" }, "http-post请求": { "prefix": "httppost", "body": [ "this.\\$http({", "url: this.\\$http.adornUrl(''),", "method: 'post',", "data: this.\\$http.adornData(data, false)", "}).then(({ data }) => { });" ], "description": "httpPOST请求" } } ``` 更多详细说明见: https://blog.csdn.net/z772330927/article/details/105730430/ ### 5. vscode快捷键 ctrl+shift+f 全局搜索 alt+shift+f 格式化代码 ### 6. 关闭eslint的语法检查 ![image-20200428171043110](images/image-20200428171043110.png) ```json ``` ### 7. 安装mybatisx插件 在Marketplace中搜索“mybatisx”,安装后重启IDEA,使用时会自动在@Mapper标注的接口上,产生小图标,然后alt+enter,generate statement,就会自动的在xml文件中生成SQL。 ![1588730028929](images/1588730028929.png) ### 8. mysql的批量删除 ```sql DELETE FROM `pms_attr_attrgroup_relation` WHERE (attr_id= ? AND attr_group_id ) OR (attr_id= ? AND attr_group_id ) ``` ### 9. String.join ```java java.lang.String @NotNull public static String join(@NotNull CharSequence delimiter, @NotNull Iterable elements) ``` Returns a new String composed of copies of the CharSequence elements joined together with a copy of the specified delimiter. 返回一个由CharSequence元素的副本和指定分隔符的副本组成的新字符串。 For example, List strings = new LinkedList<>(); strings.add("Java");strings.add("is"); strings.add("cool"); String message = String.join(" ", strings); //message returned is: "Java is cool" Set strings = new LinkedHashSet<>(); strings.add("Java"); strings.add("is"); strings.add("very"); strings.add("cool"); String message = String.join("-", strings); //message returned is: "Java-is-very-cool" Note that if an individual element is null, then "null" is added. 注意,如果单个元素为null,则添加“null”。 Params: delimiter – a sequence of characters that is used to separate each of the elements in the resulting String 用于分隔结果字符串中的每个元素的字符序列 elements – an Iterable that will have its elements joined together. 将其元素连接在一起的可迭代的。 Returns: a new String that is composed from the elements argument 由elements参数组成的新字符串 Throws: NullPointerException – If delimiter or elements is null ```java public static String join(CharSequence delimiter, Iterable elements) { Objects.requireNonNull(delimiter); Objects.requireNonNull(elements); StringJoiner joiner = new StringJoiner(delimiter); for (CharSequence cs: elements) { joiner.add(cs); } return joiner.toString(); } ``` 能够看到实际上它就是通过创建StringJoiner,然后遍历elements,加入每个元素来完成的。 StringJoiner ```java java.util public final class StringJoiner extends Object ``` StringJoiner is used to construct a sequence of characters separated by a delimiter and optionally starting with a supplied prefix and ending with a supplied suffix. tringJoiner用于构造由分隔符分隔的字符序列,可以选择以提供的前缀开始,以提供的后缀结束。 Prior to adding something to the StringJoiner, its sj.toString() method will, by default, return prefix + suffix. However, if the setEmptyValue method is called, the emptyValue supplied will be returned instead. This can be used, for example, when creating a string using set notation to indicate an empty set, i.e. "{}", where the prefix is "{", the suffix is "}" and nothing has been added to the StringJoiner. 在向StringJoiner添加内容之前,它的sj.toString()方法在默认情况下会返回前缀+后缀。但是,如果调用setEmptyValue方法,则返回所提供的emptyValue。例如,当使用set符号创建一个字符串来表示一个空集时,可以使用这种方法。“{}”,其中前缀是“{”,后缀是“}”,没有向StringJoiner添加任何内容。 apiNote: The String "[George:Sally:Fred]" may be constructed as follows: ```java StringJoiner sj = new StringJoiner(":", "[", "]"); sj.add("George").add("Sally").add("Fred"); String desiredString = sj.toString(); ``` A StringJoiner may be employed to create formatted output from a java.util.stream.Stream using java.util.stream.Collectors.joining(CharSequence). For example: 使用StringJoiner从java.util.stream创建格式化输出流,使用java.util.stream.Collectors.joining (CharSequence进行)。例如: ``` List numbers = Arrays.asList(1, 2, 3, 4); String commaSeparatedNumbers = numbers.stream() .map(i -> i.toString()) .collect(Collectors.joining(", ")); ``` 通过分析源码发现,在“”内部维护了一个StringBuilder,所有加入到它内部的元素都会先拼接上分割符,然后再拼接上加入的元素 ```java public StringJoiner add(CharSequence newElement) { prepareBuilder().append(newElement); return this; } ``` ```java private StringBuilder prepareBuilder() { if (value != null) { value.append(delimiter); } else { value = new StringBuilder().append(prefix); } return value; } ``` ### 10. 在Service中微服务比较多的时候,可以配置将一些微服务放置到compound中,组成一个小组 ![image-20200508222508833](images/image-20200508222508833.png) 以后再运行时,直接选择这个compound即可很方便的运行或停止一组微服务: ![image-20200508223524543](images/image-20200508223524543.png) 另外可以单独为每个微服务,设置需要的运行时最大堆内存大小: ![image-20200508222812353](images/image-20200508222812353.png) ### 11. mysql的dateTime和timestamp的区别? [MySQL中datetime和timestamp的区别及使用](https://www.cnblogs.com/mxwz/p/7520309.html) **TIMESTAMP和DATETIME的相同点:** 1> 两者都可用来表示YYYY-MM-DD HH:MM:SS[.fraction]类型的日期。 **TIMESTAMP和DATETIME的不同点:** 1> 两者的存储方式不一样 对于TIMESTAMP,它把客户端插入的时间从当前时区转化为UTC(世界标准时间)进行存储。查询时,将其又转化为客户端当前时区进行返回。 而对于DATETIME,不做任何改变,基本上是原样输入和输出。 2> 两者所能存储的时间范围不一样 timestamp所能存储的时间范围为:'1970-01-01 00:00:01.000000' 到 '2038-01-19 03:14:07.999999'。 datetime所能存储的时间范围为:'1000-01-01 00:00:00.000000' 到 '9999-12-31 23:59:59.999999'。 总结:TIMESTAMP和DATETIME除了存储范围和存储方式不一样,没有太大区别。当然,对于跨时区的业务,TIMESTAMP更为合适。 https://www.cnblogs.com/Jashinck/p/10472398.html ### 12. SpringBoot中的事务 https://blog.csdn.net/Z__Sheng/article/details/89489053 ### 13. IDEA RESTFUll clinet [IntelliJ IDEA 使用 rest client](https://blog.csdn.net/qq_37502106/article/details/103183492) ### ## FAQ ### 1. TypeError: _vm.previewHandle is not a function