分布式笔记

分布式笔记

这是个人分布式项目的笔记,有点乱

1.准备工作

1.1Pom文件

父:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.jt</groupId>
  <artifactId>jt</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>pom</packaging>
  
  <parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.1.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
		<java.version>1.8</java.version>
		<!--指定插件版本 -->
		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
		<!--跳过测试类打包 -->
		<skipTests>true</skipTests>
 </properties>

	<dependencies>
		<!--在webjar包的内部关联整合了所有的springMVC的jar包信息. 所以只需要引入一个jar包,则可以关联整合所有的有关mvc的依赖包信息 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!--添加属性注入依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
		<!--支持热部署 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
		</dependency>
		<!--引入插件lombok 自动的set/get/构造方法插件 -->
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<!--引入数据库驱动 -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<!--springBoot数据库连接 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jdbc</artifactId>
		</dependency>
		<!--spring整合mybatis-plus -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<!--最新版本包 -->
			<version>3.3.2</version>
		</dependency>	
		<!--springBoot整合JSP添加依赖  -->
		<!--servlet依赖 -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
		</dependency>

		<!--jstl依赖 -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
		</dependency>

		<!--使jsp页面生效 -->
		<dependency>
			<groupId>org.apache.tomcat.embed</groupId>
			<artifactId>tomcat-embed-jasper</artifactId>
		</dependency>
		<!-- 引入aop支持 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
		<!--spring整合redis -->
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
		</dependency>	
		<!--添加httpClient jar包 -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
		</dependency>
		
		<!--引入dubbo配置 -->
		<dependency>
			<groupId>com.alibaba.boot</groupId>
			<artifactId>dubbo-spring-boot-starter</artifactId>
			<version>0.2.0</version>
		</dependency>
		<!--添加Quartz的支持 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-quartz</artifactId>
		</dependency>
	</dependencies>
	<modules>
		<module>jt-common</module>
		<module>jt-manager</module>
		<module>jt-web</module>
		<module>jt-sso</module>
		<module>jt-cart</module>
		<module>jt-order</module>
	</modules>
</project>

manager ,子pom

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>com.jt</groupId>
    <artifactId>jt</artifactId>
    <version>0.0.1-SNAPSHOT</version>
  </parent>
  <artifactId>jt-manager</artifactId>
  <packaging>war</packaging>
  <dependencies>
		<!--2.添加依赖 -->
		<dependency>
			<groupId>com.jt</groupId>
			<artifactId>jt-common</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
	</dependencies>
   <!--3.添加插件 -->
	<!--负责项目打包 更新 maven操作相关的配置 必须添加 -->
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

1.2 application.yml

server:
  port: 8091
  servlet:
    context-path: /
spring:
  datasource:
    #引入druid数据源
    #type: com.alibaba.druid.pool.DruidDataSource
    #driver-class-name: com.mysql.jdbc.Driver
#    url: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    url: jdbc:mysql://192.168.140.200:8066/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
    username: root
    password: root
#    password: 123
  mvc:
    view:
      prefix: /WEB-INF/views/
      suffix: .jsp
#mybatis-plush配置
mybatis-plus:
  type-aliases-package: com.jt.pojo
  mapper-locations: classpath:/mybatis/mappers/*.xml
  configuration:
    map-underscore-to-camel-case: true
logging:
  level: 
    com.jt.mapper: debug
dubbo:
  scan:
    basePackages: com.jt
  application:
    name: provider-item
  registry:
    address: zookeeper://192.168.140.200:2181?backup=192.168.140.200:2182,192.168.140.200:2183
  protocol:
    name: dubbo
    port: 20881 #每个服务都应该有自己独立的端口            

2. 涉及知识点集合

2.1@Value方式赋值

适用于单个属性赋值,在application.yml文件赋值

jdbc:
  username: root
  password: root

在实现类中引入

	//@Value作用: 从spring容器中找到具体的key,为属性赋值.
	@Value("${jdbc.username}") //spel表达式 spring提供
	private String username;	//定义数据库用户名
	
	@Value("${jdbc.password}")
	private String password;	//定义数据库密码

2.2 批量赋值

多个属性赋值可用以下方式,注意前缀

@ConfigurationProperties(prefix = "jdbc")  //定义属性的前缀
@Data
public class JDBCController2 {
	//批量为属性赋值时,要求配置文件的属性与类中的属性名称必须一致. 自动的赋值.
	private String username;	//定义数据库用户名
	private String password;	//定义数据库密码
	//为属性赋值时,一定会调用对象的set方法.
	@RequestMapping("/getMsgPrefix")
	public String getMsgValue() {
		return username+"|"+password;
	}
}

2.3 @PropertySource,额外配置文件赋值

在这里插入图片描述

@RestController //保证返回的数据转化为JSON
//properties与spring容器建立关系,指定pro文件之后,进行加载. 默认的加载策略,采用ISO-8859-1编码
//如果其中包含中文,则应该采用utf-8格式编码.
@PropertySource(value = "classpath:/properties/jdbc.properties",encoding = "UTF-8")
public class JDBCController {
	//@Value作用: 从spring容器中找到具体的key,为属性赋值.
	@Value("${jdbc2.username}") //spel表达式 spring提供
	private String username;	//定义数据库用户名
	@Value("${jdbc2.password}")
	private String password;	//定义数据库密码
	@RequestMapping("/getMsg")
	public String getMsg() {
		this.username = "root";
		this.password = "root";
		return username+"|"+password;
	}
	
	@RequestMapping("/getMsgValue")
	public String getMsgValue() {
		
		return username+"|"+password;
	}
	

2.4 多环境配置文件

这篇文章已实验,不再多描述

https://blog.csdn.net/Coder_Qiang/article/details/83306780

2.5 Mybatis-plus API说明

2.5.1POM
<!--spring整合mybatis-plus -->
		<dependency>
			<groupId>com.baomidou</groupId>
			<artifactId>mybatis-plus-boot-starter</artifactId>
			<version>3.2.0</version>
	</dependency>
2.5.2 application.yml
#切换为Mybatisplus的配置   
mybatis-plus:
  # 定义别名包
  type-aliases-package: com.jt.demo.pojo
  # 批量导入mapper映射文件
  mapper-locations: classpath:/mybatis/mappers/*.xml
  #开启驼峰映射
  configuration:
    map-underscore-to-camel-case: true
2.5.3 POJO
@JsonIgnoreProperties(ignoreUnknown=true) //表示JSON转化时忽略未知属性
@TableName("tb_item")
@Data
@Accessors(chain=true)
public class Item extends BasePojo{
	@TableId(type=IdType.AUTO)
	private Long id;				//商品id
	private String title;			//商品标题
	private String sellPoint;		//商品卖点信息
	private Long   price;			//商品价格 Long > dubbo
	private Integer num;			//商品数量
	private String barcode;			//条形码
	private String image;			//商品图片信息   1.jpg,2.jpg,3.jpg
	private Long   cid;				//表示商品的分类id
	private Integer status;			//1正常,2下架
	//为了满足页面调用需求,添加get方法
	public String[] getImages(){
		return image.split(",");
	}
}
2.5.4 Mapper
public interface ItemMapper extends BaseMapper<Item>{
	@Select("select * from tb_item order by updated desc limit #{start},#{rows}")
	List<Item> selectItemByPage(int start,int rows);
}

2.5.5 分页插件
@MapperScan(basePackages = "com.ma.mapper") //设置mapper接口的扫描包
@Configuration
public class MybatisPlusConfig {
    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    @Bean
    public SqlExplainInterceptor sqlExplainInterceptor(){
        SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
        List<ISqlParser> sqlParserList = new ArrayList<>();
        // 攻击 SQL 阻断解析器、加入解析链
        sqlParserList.add(new BlockAttackSqlParser());
        sqlExplainInterceptor.setSqlParserList(sqlParserList);
        return sqlExplainInterceptor;
    }
    /**
     * SQL执行效率插件
     */
    @Bean
    // 设置 dev test 环境开启,保证效率
//    @Profile({"dev","test"})
    public PerformanceInterceptor performanceInterceptor() {
        PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
        // ms 设置 SQL 执行的最大时间,如果超过了就不执行,单位为ms
        performanceInterceptor.setMaxTime(100);
        //    开启格式化支持
        performanceInterceptor.setFormat(false);
        return performanceInterceptor;
    }
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
}
2.5.6常用方法
查询所有数据
	userMapper.selectList(null);
根据Id查询
	User user=userMapper.selectById(id);
根据Id批量查询
	userMapper.selectBatchIds(Arrays.asList(2L, 3L, 4L, 100L));
查询单条
 	QueryWrapper<User> wrapper = new QueryWrapper<>();
	//查询条件
	wrapper.eq("password", "123456");
	// 查询的数据超过一条时,会抛出异常
	User user = userMapper.selectOne(wrapper);
构建条件构造器查询
    User user = new User();
    user.setName("特朗普");
    QueryWrapper<User> queryWrapper = new QueryWrapper<>(user);
    List<User> userList = userMapper.selectList(queryWrapper);
分页查询
//import com.baomidou.mybatisplus.core.metadata.IPage;
//import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
	Page<User> page = new Page<>(3, 1); //查询第一页,查询1条数据
	QueryWrapper<User> wrapper = new QueryWrapper<>();
    //设置查询条件
    wrapper.like("email", "itcast");
    IPage<User> iPage = this.userMapper.selectPage(page, wrapper);
    System.out.println("数据总条数: " + iPage.getTotal());
    System.out.println("数据总页数: " + iPage.getPages());
    System.out.println("当前页数: " + iPage.getCurrent());
    List<User> records = iPage.getRecords();
条件查询
allEq
	 Map<String,Object> params = new HashMap<>();
        params.put("name", "李四");
        params.put("age", "20");
        params.put("password", null);
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        //SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE password IS NULL AND name = ? AND age = ?
//        wrapper.allEq(params);
        //SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? AND age = ?
//        wrapper.allEq(params, false);

        //SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE age = ?
//        wrapper.allEq((k, v) -> (k.equals("age") || k.equals("id")) , params);
        //SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name = ? AND age = ?
        wrapper.allEq((k, v) -> (k.equals("age") || k.equals("id") || k.equals("name")) , params);

        List<User> users = this.userMapper.selectList(wrapper);
eq查询
        //eq 等于 =
        //        ne 不等于 <>
        //        gt 大于 >
        //        ge 大于等于 >=
        //        lt 小于 <
        //        le 小于等于 <=
        //        between BETWEEN 值1 AND 值2
        //        notBetween NOT BETWEEN 值1 AND 值2
        //        in 字段 IN (value.get(0), value.get(1), ...)
        QueryWrapper<User> wrapper = new QueryWrapper<>();

        //SELECT id,user_name,password,name,age,email FROM tb_user WHERE password = ? AND age >= ? AND name IN (?,?,?)
        wrapper.eq("password", "123456")
                .ge("age", 20)
                .in("name", "李四", "王五", "赵六");

        List<User> users = this.userMapper.selectList(wrapper);
三种模糊查询
		QueryWrapper<User> wrapper = new QueryWrapper<>();
        // SELECT id,user_name,name,age,email AS mail FROM tb_user WHERE name LIKE ?
        // 参数:%五(String)
        wrapper.likeLeft("name", "五");

        List<User> users = this.userMapper.selectList(wrapper);
查询倒叙
		QueryWrapper<User> wrapper = new QueryWrapper<>();
        //按照年龄倒序排序
        // SELECT id,user_name,name,age,email AS mail FROM tb_user ORDER BY age DESC
        wrapper.orderByDesc("age");

        List<User> users = this.userMapper.selectList(wrapper);
or查询
		wrapper.eq("name", "王五").or().eq("age", 21);
指定查询字段
 		wrapper.eq("name", "王五")
                .or()
                .eq("age", 21)
                .select("id","name","age"); //指定查询的字段
查询条数
	QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.gt("age", 20); // 条件:年龄大于20岁的用户
    // 根据条件查询数据条数
    Integer count = this.userMapper.selectCount(wrapper);
插入
    User user = new User();
    user.setName("特朗普");
    user.setAge(60);
    user.setSex("男");
    int result = userMapper.insert(user); //返回的result是受影响的行数,并不是自增后的id
    boolean insertSelective(T entity); //选择性插入,null字段不插入
    boolean insertBatch(List<T> entityList); //批量插入
修改
	//根据id更新,更新不为null的字段
    userMapper.updateById(user);
根据条件更新
	User user = new User();
    user.setAge(20); //更新的字段
    user.setPassword("8888888");
	QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("user_name", "zhangsan"); //匹配user_name = zhangsan 的用户数据
	//根据条件做更新
    int result = this.userMapper.update(user, wrapper);
UpdateWrapper更新
	UpdateWrapper<User> wrapper = new UpdateWrapper<>();
        wrapper.set("age", 21).set("password", "999999") //更新的字段
                .eq("user_name", "zhangsan"); //更新的条件
        //根据条件做更新
        int result = this.userMapper.update(null, wrapper);    
删除
// 根据id删除数据
	int result = this.userMapper.deleteById(9L);
// 根据map删除数据,多条件之间是and关系
	Map<String,Object> map = new HashMap<>();
    map.put("user_name", "zhangsan");
    map.put("password", "999999");
	int result = this.userMapper.deleteByMap(map);
// 根据包装条件做删除
	User user = new User();
    user.setPassword("123456");
    user.setUserName("caocao");
	QueryWrapper<User> wrapper = new QueryWrapper<>(user);
	int result = this.userMapper.delete(wrapper);
// 根据id批量删除数据
    int result = this.userMapper.deleteBatchIds(Arrays.asList(10L, 11L));	
2.5.7 修改POJO后的特殊方法

POJO

@Data
@NoArgsConstructor
@AllArgsConstructor
//@TableName("tb_user")
public class User extends Model<User> {
    //@TableId(type=IdType.AUTO)
    private Long id;
    private String userName;
    @TableField(select=false,fill = FieldFill.INSERT)
    private String password;
    private String name;
    private Integer age;
    @TableField(value = "email")//数据库中为email
    private String mail;
    @TableField(exist = false)
    private String address;//数据库中不存在

    @Version
    private Integer version;
    @TableLogic//逻辑删除,1删除了,2未删除
    private Integer deleted;
    //枚举
    private SexEnum sex;
}

特殊方法:

添加拦截器

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    /**
        * 插入数据时填充
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        // 先获取到password的值,再进行判断,如果为空,就进行填充,如果不为空,就不做处理
        Object password = getFieldValByName("password", metaObject);
        if(null == password){
           setFieldValByName("password", "888888", metaObject);
        }
    }
    /**
     * 更新数据时填充
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {

    }
}
自动注入密码88888
		User user = new User();
        user.setAge(20);
        user.setMail("test@itcast.cn");
        user.setName("曹操");
        user.setUserName("caocao");
//        user.setPassword("123456");
        int result = userMapper.insert(user); //返回的result是受影响的行数,并不是自增后的id
        System.out.println("result = " + result);
        System.out.println(user.getId()); //自增后的id会回填到对象中

逻辑删除:

userMapper.deleteById(2L);

枚举:

		User user = new User();
        user.setName("貂蝉");
        user.setUserName("diaochan");
        user.setAge(20);
        user.setMail("diaochan@itast.cn");
        user.setVersion(1);
        user.setSex(SexEnum.WOMAN);
        int result = this.userMapper.insert(user);

2.6 restFul说明

可以用来动态的接收url中的参数.之后完成业务调用,可以通过不同的请求类型来标识不同的业务需求.

用restFul风格,可以简化用户url的写法.

type=“GET” 查询操作 * type=“POST” 新增操作 * type=“PUT” 更新操作 * type="DELETE"删除操作

2.7 文件上传

2.7.1 简单模式
@RestController
public class FileController {
    /**
     * url地址: http://localhost:8091/file
     * 请求参数: fileImage
     * 返回值:   文件上传成功
     * 
     * 类型:MultipartFile
     * 实现步骤:
     *      1.接收资源文件
     *      2.准备文件上传目录
     *      3.准备文件上传的全路径   目录/文件名称
     */
    @RequestMapping("/file")
    public String  file(MultipartFile fileImage){
       //2.文件文件上传的目录
        String fileDirPath = "D:/JT-SOFT/images";
        File dirFile = new File(fileDirPath);
        //判断文件目录是否存在
        if(!dirFile.exists()){
            //如果文化间目录没有,则应该新建目录
            dirFile.mkdirs(); //创建多级目录
        }
        //3.准备文件上传的全路径.  路径+文件名称
        String fileName = fileImage.getOriginalFilename();  //文件名称.后缀   123.jgp
        File realFile = new File(fileDirPath+"/"+fileName);
        //将字节信息输出到文件中.
        try {
            fileImage.transferTo(realFile); //实现文件上传
            return "文件上传成功!!!";
        } catch (IOException e) {
            e.printStackTrace();
            return "文件上传失败!!!";
        }
    }
}
2.7.2 以日期为目录上传文件,完整流程

POJO对象

@Data
@Accessors(chain = true)	//链式加载
@NoArgsConstructor
@AllArgsConstructor
public class ImageVO implements Serializable {
    private Integer error;  //确认是否有错误   0正常    1错误
    private String  url;    //图片访问的虚拟地址.
    private Integer width;  //宽度
    private Integer height; //高度
    public static ImageVO fail(){
        return new ImageVO(1, null, null, null);
    }
    public static ImageVO success(String url){
        return new ImageVO(0, url, null, null);
    }
    public static ImageVO success(String url,Integer width,Integer height){

        return new ImageVO(0, url, width, height);
    }
}

properties配置文件

#image.localDir=D:/JT-SOFT/images
image.localDir=/usr/local/src/images
image.imageTypes=.jpg,.png,.git,.jpeg
image.imageUrl=http://image.jt.com

工具类

@Component
@PropertySource("classpath:/properties/image.properties")
public class ImageTypeUtil {
	@Value("${image.imageTypes}")
	private String imageTypes;
	
	private Set<String> typeSet=new HashSet<>();
//	PostConstruct 注释用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化。此方法必须在将类放入服务之前调用。
//	支持依赖关系注入的所有类都必须支持此注释。即使类没有请求注入任何资源,用 PostConstruct 注释的方法也必须被调用。只有一个方法可以用此注释进行注释。
//	PreDestroy 用与在依赖注入完成之前的方法前面执行,
	@PostConstruct
	public void init() {
		String[] typeArray=imageTypes.split(",");
		for(String type : typeArray) {
			typeSet.add(type);
		}
	}
	public Set<String> getImageType() {
		return typeSet;
	}
	// 第一种方案
//	private static Set<String> typeSet=new HashSet<>();
//
//	static {
//		typeSet.add(".jpg");
//		typeSet.add(".png");
//		typeSet.add(".gif");
//		typeSet.add(".bmp");
//		typeSet.add(".jpeg");
//	}	
//	
//	public static Set getImageType() {
//		return typeSet;
//	}
}

controller

/**
     * 实现图片上传操作.
     * url地址:http://localhost:8091/pic/upload?dir=image
     * 参数信息: uploadFile
     * 返回值: ImageVO对象
     */
    @RequestMapping("/pic/upload")
    public ImageVO  upload(MultipartFile uploadFile){
        return fileService.upload(uploadFile);
    }

service实现类

@Service
@PropertySource(value = "classpath:/properties/image.properties")
public class FileServiceImpl implements FileService{
	@Value("${image.localDir}")
	//"D:/JT-SOFT/images"
	private String localDir;
	@Value("${image.imageUrl}")
	private String imageUrl;
	@Autowired
	private ImageTypeUtil imageTypeUtil;
	@Override
	public ImageVO upload(MultipartFile uploadFile) {
		//初始化图片类型集合
//		List<String> typeList=new ArrayList<>();
		//静态代码读取
//		Set<String> typeSet = ImageTypeUtil.getImageType();
		Set<String> typeSet = imageTypeUtil.getImageType();
		
		//获取类型
		String fileName=uploadFile.getOriginalFilename();
		//转小写
		fileName=fileName.toLowerCase();
//		String fileType=StringUtils.getFilenameExtension(fileName);
		int index=fileName.lastIndexOf(".");
		//包含.
		String fileType=fileName.substring(index);
		if(!typeSet.contains(fileType)) {
			return ImageVO.fail();
		}
		//准备文件上传的目录
		String dateDir=new SimpleDateFormat("/yyyy/MM/dd/").format(new Date());
		String dirPath= localDir+dateDir;
		File dirFile = new File(dirPath);
		if(!dirFile.exists()) {
			dirFile.mkdirs();
		}
		//重新制定文件名
		String uuid= UUID.randomUUID().toString();
		String realFileName= uuid+fileType;
		//上传
		File imageFile = new File(dirPath+realFileName);
		System.out.println(imageFile.getAbsolutePath());
		try {
			uploadFile.transferTo(imageFile);
			String url=imageUrl+dateDir+realFileName;
			return ImageVO.success(url);
		} catch (IllegalStateException | IOException e) {
			e.printStackTrace();
			return ImageVO.fail();
		} 
	}
}

2.8 jackson工具类

public class ObjectMapperUtil {
	//定义一个常量对象
		private static final ObjectMapper MAPPER = new ObjectMapper();
		
		//1.将对象转化为json串
		public static String toJSON(Object target) {
			if(target ==null) {
				throw new NullPointerException("数据不能为空");
			}
			String json = null;
			try {
				json = MAPPER.writeValueAsString(target);
			} catch (JsonProcessingException e) {
				e.printStackTrace();
				//将检查异常,转化为运行时异常!!!!
				throw new RuntimeException();
			}
			return json;
		}
		
		//2.将json串按照指定的类型转化为对象
		//实现:传递什么类型,就返回什么对象
		// <T> 定义泛型
		public static <T> T  toObj(String json,Class<T> target) {
			if(StringUtils.isEmpty(json)|| target == null) {
				throw new NullPointerException("数据不能为空 ");
			}
			
			T t = null;
			try {
				t = MAPPER.readValue(json, target);
			} catch (JsonProcessingException e) {
				e.printStackTrace();
				throw new RuntimeException();
			}
			return t;
		}

}

2.9 redis缓存

配置类省略

自定义缓存注解:

@Target(ElementType.METHOD)//方法上
@Retention(RetentionPolicy.RUNTIME)//运行时有效
public @interface CacheFind {
	//key前缀
	public String key();
	//保存时间,单位秒
	public int seconds() default 0;
}

切面类:

@Component
@Aspect
public class CacheAOP {
//	jt-manager/src/main/java/com/jt/service/ItemCatServiceImpl.java
//	@Pointcut("bean(itemCatServiceImpl)")
//	@Pointcut("within(com.jt.service..*)")
	// 返回值类型 包名.类名(参数)
//	@Pointcut("execution(* com.jt.service..*.*(..))")
//	@Pointcut("@annotation(com.jt.anno.CacheFind)")
//	public void pointCut() {
//		
//	}
	@Autowired(required = false)
//	private Jedis jedis;
//	private ShardedJedis jedis;
	private JedisCluster jedis;
	
	//正式业务
//	@Around("@annotation(com.jt.anno.CacheFind)")
	@SuppressWarnings("unchecked")
	@Around("@annotation(cacheFind)")
	public Object around(ProceedingJoinPoint pj,CacheFind cacheFind) {
		String key=cacheFind.key();
		//拼接redis的key
		String parentId = pj.getArgs()[0].toString();
		key+="::"+parentId;
		System.out.println(key);
		Object result=null;
		if(jedis.exists(key)) {
			//获取方法返回类
			MethodSignature signature = (MethodSignature) pj.getSignature();
			result=ObjectMapperUtil.toObj(jedis.get(key), signature.getReturnType());
		}else {
			try {
				 result=pj.proceed();
				 //缓存
				 String json=ObjectMapperUtil.toJSON(result);
				 int seconds=cacheFind.seconds();
				 if(seconds >0) {
					 jedis.setex(key, seconds, json);
				 }else {
					 jedis.set(key, json);
				 }
			} catch (Throwable e) {
				e.printStackTrace();
				throw new RuntimeException();
			}
		}
		return result;
		
	}


}

2.10 redis常见问题

2.10.1 缓存穿透

特点: 用户高并发环境下,访问数据库中根本不存在的数据.
影响:由于用户高并发访问,则数据库可能存在宕机的风险.

方案: 1. API网关限流 2 . 限定网关访问次数 3 . 布隆过滤器 4 . 查询为空也缓存,过期时间设置短

2.10.2 缓存击穿

key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。

方案 : 1. 设置永不过期 2 . redis本身是集群,尽量保证集群数量足够

2.10.3 缓存雪崩

缓存雪崩:当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。

方案 : 设置随机时间,多级缓存

2.11 持久化

2.11.1 RDB模式

1.RDB模式采用定期持久化的方式. 风险:可能丢失数据.
2.RDB模式记录的是当前Redis的内存记录快照. 只记录当前状态. 持久化效率最高的
3.RDB模式是默认的持久化方式.
2.11.2 AOF模式
1.AOF模式默认是关闭状态 如果需要则手动开启.
2.AOF能够记录程序的执行过程 可以实现数据的实时持久化. AOF文件占用的空间较大.回复数据的速度较慢.
3.AOF模式开启之后.RDB模式将不生效.

2.12 内存策略

2.12.1 LRU算法
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
维度: 自上一次使用的时间T
最为理想的内存置换算法.
2.12.2 LFU算法
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
least frequently used (LFU) page-replacement algorithm
即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
维度: 引用次数
2.12.3 随机算法

2.13 内存优化策略

1.volatile-lru 在设定了超时时间的数据, 采用lru算法进行删除.
2.allkeys-lru 所有数据采用lru算法
3.volatile-lfu 在设定了超时时间的数据, 采用LFU算法进行删除.
4.allkeys-lfu 所有数据采用LFU算法
5.volatile-random 设定超时时间数据采用随机算法
6.allkeys-random 所有数据采用随机算法
7.volatile-ttl 设定了超时时间的数据 根据ttl规则删除. 将剩余时间少的提前删除
8.noeviction 内存满了 不做任何操作.报错返回.

2.14 全局cros

@Configuration
public class CorsConfig implements WebMvcConfigurer{
	
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		// TODO Auto-generated method stub
		//Origins 源 http: // www.aa.com
		//methods 请求类型 get put delete post
		//允许携带cookie和session
		// .maxAge 允许跨域的持续时间
		registry.addMapping("/**")
//		.allowedOrigins("http://www.aa.com")
		.allowedOrigins("*")
		.allowedMethods("GET","POST","DELETE","HEAD","PUT")
		.allowCredentials(true);
		WebMvcConfigurer.super.addCorsMappings(registry);
	}
}

2.15 dubbo

2.15.1 pom
<dependency>
	<groupId>com.alibaba.boot</groupId>
	<artifactId>dubbo-spring-boot-starter</artifactId>
	<version>0.2.0</version>
</dependency>
2.15.2 序列化对象
@Data
@Accessors(chain=true)
@TableName //如果对象名称与表名一致,则可以省略不写.
public class User implements Serializable{ 
	//dubbo协议中传输的对象必须序列化
	private static final long serialVersionUID = 1L;
	@TableId(type=IdType.AUTO)
	private Integer id;
	private String name;
	private Integer age;
	private String sex;

}
2.15.3 第三方接口
public interface UserService {
	//查询全部的用户信息
	List<User> findAll();
	//新增用户入库操作.
	@Transactional
	void saveUser(User user);
}
2.15.4 实现类
//import com.alibaba.dubbo.config.annotation.Service;
@Service(timeout=3000)	//3秒超时 使用dubbo的注解
//@org.springframework.stereotype.Service//将对象交给spring容器管理
public class UserServiceImpl implements UserService {  //实现公共的第三方	
	@Autowired
	private UserMapper userMapper;
	/*
		userService接口只是定义规范.不负责具体的实现.将来由服务的提供者实现该功能.
	* */
	@Override
	public List<User> findAll() {
		return userMapper.selectList(null);
	}
	
	@Override
	public void saveUser(User user) {		
		userMapper.insert(user);
	}
}

2.15.5 application.yml
#关于Dubbo配置   
dubbo:
  scan:
    basePackages: com.xx    #指定dubbo的包路径 扫描dubbo的注解
  application:              #应用名称
    name: provider-user     #相同的接口 服务名称必定一致.  不同的接口服务名称一定不一致.
  registry:     #配置注册中心
    address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183
  protocol:  #指定协议
    name: dubbo  #使用dubbo协议(tcp-ip)  web-controller直接调用sso-Service
    port: 20880  #每一个服务都有自己特定的端口 不能重复.

2.16 cookie共享

 /**
     * 实现用户退出操作
     * url:http://www.jt.com/user/logout.html
     * 返回值: 重定向到系统首页.
     * 目的:  删除redis. 删除Cookie
     * 前提:  需要获取cookie的key和value
     */
    @RequestMapping("/logout")
    public String logout(HttpServletRequest request,HttpServletResponse response){
        String jtTicket = null;
        //1.如何获取cookie中的数据?
        Cookie[] cookies = request.getCookies();
        //2.校验Cookie数据是否为null
        if(cookies !=null && cookies.length>0){
            for(Cookie cookie : cookies){
                if(TICKET.equalsIgnoreCase(cookie.getName())){
                    jtTicket = cookie.getValue();
                    //业务需要提前删除Cookie
                    cookie.setMaxAge(0);
                    cookie.setPath("/");
                    cookie.setDomain("jt.com");
                    response.addCookie(cookie);
                    break;
                }
            }
        }
        //2.校验数据是否有效
        if(!StringUtils.isEmpty(jtTicket)){
            //如果数据不为null,则开始执行退出操作.
            jedisCluster.del(jtTicket); //根据key,删除Redis中的记录
            //删除cookie.
        }
        return "redirect:/";
    }

2.17 Threadlocal

package com.jt.util;

import com.jt.pojo.User;

public class UserThreadLocal {

    //1.定义本地线程变量!!!!!
    private static ThreadLocal<User> threadLocal = new ThreadLocal<>();
                   //ThreadLocal<Map>
    //2.定义数据新增的方法
    public static void set(User user){

        threadLocal.set(user);
    }

    //3.获取数据
    public static User get(){

        return threadLocal.get();
    }

    //4.移除方法 使用threadLocal时切记将数据移除.否则极端条件下,容易产出内存泄露的问题
    public static void remove(){

        threadLocal.remove();
    }
	 //实现数据的移除
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        UserThreadLocal.remove();
    }
}


                cookie.setDomain("jt.com");
                response.addCookie(cookie);
                break;
            }
        }
    }
    //2.校验数据是否有效
    if(!StringUtils.isEmpty(jtTicket)){
        //如果数据不为null,则开始执行退出操作.
        jedisCluster.del(jtTicket); //根据key,删除Redis中的记录
        //删除cookie.
    }
    return "redirect:/";
}

### 2.17 Threadlocal

package com.jt.util;

import com.jt.pojo.User;

public class UserThreadLocal {

//1.定义本地线程变量!!!!!
private static ThreadLocal<User> threadLocal = new ThreadLocal<>();
               //ThreadLocal<Map>
//2.定义数据新增的方法
public static void set(User user){

    threadLocal.set(user);
}

//3.获取数据
public static User get(){

    return threadLocal.get();
}

//4.移除方法 使用threadLocal时切记将数据移除.否则极端条件下,容易产出内存泄露的问题
public static void remove(){

    threadLocal.remove();
}
 //实现数据的移除
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    UserThreadLocal.remove();
}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值