Docker 安装
CentOS 版本官方安装文档: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
设置 yum 库 ```shell $ sudo yum install -y yum-utils
官方源,比较慢
$ sudo yum-config-manager \ —add-repo \ https://download.docker.com/linux/centos/docker-ce.repo
建议使用阿里源
$ sudo yum-config-manager \ —add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 安装
```shell
$ sudo yum install docker-ce docker-ce-cli containerd.io
启动
$ sudo systemctl start docker
设置开机自启
$ systemctl enable docker
测试
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
$ docker -v
Docker version 19.03.12, build 48a66213fe
配置镜像阿里云加速
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://bcozo2zh.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
Docker 安装 Mysql
拉取 Mysql 镜像
# 注意只能用 5.7 版本,其他版本的操作不一样
docker pull mysql:5.7
运行容器,
\
代表换行docker run -p 3306:3306 --name mysql \
-v /www/docker/mysql/log:/var/log/mysql \
-v /www/docker/mysql/data:/var/lib/mysql \
-v /www/docker/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=123 \
-d mysql
配置字符编码
[mysqld]
default-character-set = utf8
# character_set_server = utf8
Docker 安装 Redis
拉取 Redis 镜像
docker pull redis
运行容器
docker run -p 8883:6379 --name redis \
-v /www/docker/redis/data:/data \
-v /www/docker/redis/conf/redis.conf:/etc/redis/redis.conf \
-d redis redis-server /etc/redis/redis.conf
使用redis 镜像执行 redis-cli 命令连接
docker exec -it redis redis-cli
配置持久化
在 redis/conf/redis.conf 中加入以下内容,然后重启容器
appendonly yes
想知道 redis 的更多配置可以参照官方文档
配置 Maven 与 Java
$ mvn -v
Apache Maven 3.6.3 (cecedd343002696d0abb50b32b541b8a6ba2883f)
Maven home: /Library/apache-maven-3.6.3
Java version: 1.8.0_221, vendor: Oracle Corporation, runtime: /Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre
Default locale: zh_CN, platform encoding: UTF-8
OS name: "mac os x", version: "10.14.4", arch: "x86_64", family: "mac"
$ java -v
Unrecognized option: -v
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
caideMacBook-Pro:NewServer cai$
配置 IDEA 插件
lombok 插件:自动生成 getter 和 setter 方法
mybatisx 插件:从 xxxMapper.xml 快速跳转到对应接口
配置 git
$ git config --global user.name "Cai_Programmer"
$ git config --global user.email "1050440470@qq.com"
$ ssh-keygen -t rsa -C "1050440470@qq.com"
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:k/bvYXYUbvV3tuilP4LTMKdO3WnWQY76fS6luzm5pxY 1050440470@qq.com
The key's randomart image is:
+---[RSA 2048]----+
| |
| |
| ...|
| . .+o.|
| S .+o=|
| . o oo+E.O|
| .o@o.%.|
| .Bo=%oo|
| .o+=O%+|
+----[SHA256]-----+
[root@iZwz91sneajs2ji49vo4wxZ docker]# cat /root/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnP4Y9fKMNQF7pm7OqfL/hh5wYcmKqQl2op2QpLkKxvYdRmxyziDC43fXBQfbovErGSFgGrvyJkMH+sZfwntv7GbmOI2xLMJVYDGlpKBZEFRqvNTV8HrXrwTBS0w7+COm3jndwZcmzwccWwb0E81wVBTMFmRZ/BkS+BXtN92KqJnJnzI0goCnjG2PAyILSAqWx84MmOX5zAGRgy2TDHgUZ2Fjod3QmiWrR2nHzk/6lrAMXWjjtSEhIa+Odvi9IM/qSp7NKmW7gEnEH3AmOe+AZBJvq/vzIDXrrDSJw+Puc00FZyOzyhzfmO2gi3yAkIKwJ4aj11w8BkL6wHNGnv7bP 1050440470@qq.com
登陆码云 -> 进入设置 -> 安全设置 -> SSH 公钥 -> 把生成的密钥配置到码云
使用 ssh -T git@gitee.com 测试配置是否成功
$ ssh -T git@gitee.com
The authenticity of host 'gitee.com (212.64.62.174)' can't be established.
ECDSA key fingerprint is SHA256:FQGC9*****bVr17bmjey0Wc.
ECDSA key fingerprint is MD5:27:e5:d******d:1f:c1:47:a3:54:b1.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'gitee.com,212.64.62.174' (ECDSA) to the list of known hosts.
Hi 蔡俊伟! You've successfully authenticated, but GITEE.COM does not provide shell access.
使用人人开源生成器
使用 nacos 做注册中心
github 地址:https://github.com/alibaba/nacos 下载地址:https://github.com/alibaba/nacos/releases
# 解压
$ unzip nacos-server-1.0.0.zip
$ cd nacos/bin
# 启动, 默认访问路径为 localhost:8848/nacos
$ startup.sh -m standalone
- 在项目公共 pom.xml 文件中添加依赖
```xml
com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
- 在 application.yml 中添加配置
```sql
spring:
cloud:
nacos:
discovery:
# 注册中心地址
server-addr: 127.0.0.1:8848
application:
# 服务名称
name: gulimall-coupon
先启动 spring boot 服务,访问 localhost:8848/nacos,默认登陆账号和密码都是 nacos,在注册中心可以看到服务注册进来了
Spring Cloud Feign 远程调用
引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
开启 feign 功能
src/main/java/com/atguigu/gulimall/member/GulimallMemberApplication.java
// 扫描 feign 包
@EnableFeignClients(basePackages = "com.atguigu.gulimall.member.feign")
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallMemberApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallMemberApplication.class, args);
}
}
src/main/java/com/atguigu/gulimall/member/feign/CouponFeignService.java
// 服务名称
@FeignClient("gulimall-coupon")
public interface CouponFeignService {
// 请求 url
@RequestMapping("/coupon/coupon/member/list")
public R membercoupons();
}
- 调用 feign
src/main/java/com/atguigu/gulimall/member/controller/MemberController.java
@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"));
}
使用 nacos 做配置中心
使用 nacos 做配置中心,统一管理配置,修改配置不需要打包重新部署项目。
一、基本使用方法
- 在公共模块中引入依赖
gulimall-common/pom.xml
<!-- 配置中心来做配置管理 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
- 创建一个 bootstrap.properties,声明注册中心地址
gulimall-coupon/src/main/resources/bootstrap.properties
spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
- 在配置中心增加数据集(Data Id)
默认命名规则:应用名.properties
- 在控制器中获取配置信息
Spring 会优先使用配置中心的配置@RefreshScopre
:动态获取配置,每次请求都从配置中心获取配置信息@Value
:获取配置
二、命名空间与分组
1. 命名空间
默认命名空间为 public,命名空间的作用类似文件夹,起到配置分离的作用。
常规使用方法:
- 生产与开发环境各使用一个命名空间,发布的时候需要修改命名空间 id
- 每个微服务使用一个命名空间,使用分组隔离生产与开发环境配置,发布的时候需要修改分组名称
新建一个优惠券系统命名空间
在该命名空间下新建默认配置文件
在 bootstrap.properties 中声明命名空间,注意配置的是命名空间 id,不是命名空间名称 gulimall-coupon/src/main/resources/bootstrap.properties
spring.cloud.nacos.config.namespace=59927bf3-18a3-4c42-816a-bcdc528c9a5f
这样就会使用 coupon 命名空间下的 gulimall-coupon.properties 了
2. 配置集
3. 配置集 ID
4. 配置分组
功能类似命名空间,我们可以为每个配置设置一个分组
常规使用方法:新建一套分组为 dev 的配置文件,另加一套分组为 prod 的配置文件,发布的时候只要在 boostrap.properties 中修改分组名称就 ok
新建两个默认配置文件,分组名称分别为 dev 和 prod
在 bootstrap.properties 中声明分组名称
spring.cloud.nacos.config.group=prod
三、加载多个配置集
一个微服务的所有配置如果都写在同一个配置文件中会比较难维护,所以一般会把一个微服务的配置拆分到多个配置文件中,使用 nacos 也是可以加载多个配置文件的
新建多个配置文件,datasource.yml、mybatis.yml
在 bootstrap.properties 中加入配置 gulimall-coupon/src/main/resources/bootstrap.properties
spring.application.name=gulimall-coupon
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
# 配置命名空间
spring.cloud.nacos.config.namespace=59927bf3-18a3-4c42-816a-bcdc528c9a5f
# 配置分组
spring.cloud.nacos.config.group=dev
# 加载多个配置文件
# 配置 data-id
spring.cloud.nacos.config.ext-config[0].data-id=datasource.yml
# 配置分组名
spring.cloud.nacos.config.ext-config[0].group=dev
# 配置是否动态刷新配置
spring.cloud.nacos.config.ext-config[0].refresh=true
# 同上,加载第二个配置文件
spring.cloud.nacos.config.ext-config[1].data-id=mybatis.yml
spring.cloud.nacos.config.ext-config[1].group=dev
spring.cloud.nacos.config.ext-config[1].refresh=true
这样就可以加载多个配置文件了,项目配置中只需要一个 boostrap.properties,其它配置文件都迁移到配置中心
SpringCloud Gateway 网关
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/2.2.7.RELEASE/reference/html
将网关注册到注册中心,在启动类中增加注解 @EnableDiscoveryClient
@EnableDiscoveryClient
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class GulimallGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallGatewayApplication.class, args);
}
}
新建 bootstrap.properties,加入注册中心与配置中心地址
spring.application.name=gulimall-gateway
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
server.port=88
新建 application.yml,测试网关
当请求参数存在 url,且值为 baidu 时,会跳转到 https://www.baidu.com
当请求参数存在 url,且值为 qq 时,会跳转到 https://www.qq.com
spring:
cloud:
gateway:
routes:
- id: test_route
uri: https://www.baidu.com
predicates:
- Query=url,baidu
- id: qq_route
uri: https://www.qq.com
predicates:
- Query=url,qq
后台管理网关配置:
gulimall-gateway/src/main/resources/application.yml
spring:
cloud:
gateway:
routes:
- id: product_route
# lb 代表负载均衡,填写微服务名称
uri: lb://gulimall-product
predicates:
# 填写匹配规则,根据路径配置
- Path=/api/product/**
filters:
# 配置路径重写
# 例如:http://localhost:88/api/product/category/list/tree
# 重写后路径:http://server/product/category/list/tree
- RewritePath=/api/(?<segment>.*),/$\{segment}
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
在网关中配置允许跨域:
gulimall-gateway/src/main/java/com/atguigu/gulimall/gateway/config/GulimallCorsConfiguration.java
@Configuration
public class GulimallCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 配置跨域
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.setAllowCredentials(true);
// 任意路径
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
阿里云OSS对象存储
使用服务端签名直传,文件无需经过应用服务器,直接传给 oss
官方文档:https://help.aliyun.com/document_detail/31926.html?spm=a2c4g.11186623.6.1737.7d5e5a025RchiN
新建 gulimall-third-party 模块,用于封装第三方服务
引入阿里oss starter
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alicloud-oss</artifactId>
</dependency>
配置注册中心与 oss 账号信息 ```yaml spring: cloud: nacos:
discovery:
server-addr: 127.0.0.1:8848
alicloud:
access-key: LTAIQkDnEUVliI8Z
secret-key: 67Y2amus4dFKiX5kBWuXP4Jy8xMLvB
oss:
endpoint: oss-accelerate.aliyuncs.com
bucket: iep-server
application: name: gulimall-third-party
server: port: 30000
- 新建控制器 OssController,用于获取签名
```java
@RestController
public class OssController {
@Autowired
OSS ossClient;
@Value("${spring.cloud.alicloud.oss.endpoint}")
private String endpoint;
@Value("${spring.cloud.alicloud.oss.bucket}")
private String bucket;
@Value("${spring.cloud.alicloud.access-key}")
private String accessId;
@RequestMapping("/oss/policy")
public R policy() {
String host = "https://" + bucket + "." + endpoint;
// 用户上传文件时指定的前缀。
String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
String dir = format + "/";
long expireTime = 30;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = ossClient.calculatePostSignature(postPolicy);
Map<String, String> respMap = new LinkedHashMap<>();
respMap.put("accessid", accessId);
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", host);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
return R.ok().put("data",respMap);
}
}
JSR-303数据校验
作者:谁在烽烟彼岸 https://www.jianshu.com/p/554533f88370
JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。
Bean Validation 中内置的 constraint:
使用方法:
给Bean添加校验注解:
javax.validation.constraints
,并定义自己的message
提示@NotNull(groups = {AddGroup.class})
@Min(value = 0, message = "排序必须大于等于0")
private Integer sort;
控制器接收前端参数时增加
@Validated
注解,代表开启参数校验功能,如果不增加此注解则不会开启校验@RequestMapping("/save")
public R save(@Validated() @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
分组校验功能,用于多场景的复杂校验:
- 定义分组,只需要声明接口 ```java public interface UpdateGroup {}
public interface AddGroup {}
- 在校验注解中传入 groups 属性,给校验注解标注什么情况需要进行校验
```java
@NotBlank(message = "品牌名必须提交", groups = {AddGroup.class, UpdateGroup.class})
- 在控制器中的 Validated 注解中声明分组,默认没有指定分组的校验注解 @NotBlank,在分组校验情况@Validated({AddGroup.class}) 下不生效,只会在 @Validated 生效
@RequestMapping("/save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok();
}
自定义校验注解:
创建注解
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
String message() default "{com.atguigu.common.valid.ListValue.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] vals() default { };
}
创建校验器
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
private Set<Integer> set = new HashSet<>();
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
/**
* @param value 需要校验的值
* @param context
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
全局异常处理
集中处理控制器中抛出的异常,例如处理 JSR-303数据校验异常
com/atguigu/gulimall/product/exception/GulimallExceptionControllerAdvice.java/**
* 集中处理所有异常
*/
@Slf4j
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleVaildException(MethodArgumentNotValidException e) {
log.error("数据校验出现问题{},异常类型:{}", e.getMessage(), e.getClass());
BindingResult bindingResult = e.getBindingResult();
Map<String, String> errorMap = new HashMap<>();
bindingResult.getFieldErrors().forEach((fieldError) -> {
errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
});
return R.error(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data", errorMap);
}
}
SKU 与SPU
电商系统中的sku与spu