首页
关于
友链
Search
1
wlop 4K 壁纸 4k8k 动态 壁纸
1,738 阅读
2
Nacos持久化MySQL问题-解决方案
1,088 阅读
3
Docker搭建Typecho博客
916 阅读
4
滑动时间窗口算法
908 阅读
5
ChatGPT注册 OpenAI's services are not available in your country 解决方法
886 阅读
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
登录
Search
标签搜索
java
javase
docker
java8
springboot
thread
spring
分布式
mysql
锁
linux
redis
源码
typecho
centos
git
map
RabbitMQ
lambda
stream
少年
累计撰写
189
篇文章
累计收到
48
条评论
首页
栏目
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
页面
关于
友链
搜索到
189
篇与
的结果
2022-03-11
接口幂等性
接口幂等性1、什么是幂等性接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用;比如说支付场景,用户购买了商品支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,用户再次点击按钮,此时会进行第二次扣款,返回结果成功,用户查询余额返发现多扣钱了,流水记录也变成了两条...,这就没有保证接口的幂等性。2、哪些情况需要防止用户多次点击按钮用户页面回退再次提交微服务互相调用,由于网络问题,导致请求失败。feign触发重试机制其他业务情况3、什么情况下需要幂等天然幂等的:以SQL为例,有些操作是。SELECT*FROM table WHERid=?无论执行多少次都不会改变状态,是天然的幂等。UPDATE tab1 SET col1=1WHERE col2=2 无论执行成功多少次状态都是一致的,也是幂等操作。delete from user where userid=1多次操作,结果一样,具备幂等性insert into user(userid,name)values(1,a)如userid为唯一主键,即重复操作上面的业务,只会插入一条用户数据,具备幂等性。不是幂等的UPDATE tab1 SET col1=col1+1WHERE col2=2每次执行的结果都会发生变化,不是幂等的。insert into user(userid,name)values(1,a)如userid不是主键,可以重复,那上面业务多次操作,数据都会新增多条,不具备幂等性。4、幂等解决方案一、token机制1、服务端提供了发送token的接口。我们在分析业务的时候,哪些业务是存在幂等问题的,就必须在执行业务前,先去获取token,服务器会把token保存到redis中。2、然后调用业务接口请求时,把token携带过去,一般放在请求头部。3、服务器判断token是否存在redis中,存在表示第一次请求,然后删除token,继续执行业务。4、如果判断token不存在redis中,就表示是重复操作,直接返回重复标记给client,这样就保证了业务代码,不被重复执行。危险性:1、先删除token 还是后删除token;(1)先删除可能导致,业务确实没有执行,重试还带上之前token,由于防重设计导致,请求还是不能执行。(2)后删除可能导致,业务处理成功,但是服务闪断,出现超时,没有删除 token,别人继续重试,导致业务被执行两边(3)我们最好设计为先删除token,如果业务调用失败,就重新获取token 再次请求。2、Token获取、比较和删除必须是原子性(1)redis.get(token)、token.equals、redis.delltoken)如果这两个操作不是原子,可能导致,高并发下,都get 到同样的数据,判断都成功,继续业务并发执行(2)可以在redis 使用lua脚本完成这个操作if redis. call("get', KEYS[1])==ARGV[1] then return redis. call(' del", KEYS[1]) else return 0 end二、各种锁机制1、数据库悲观锁select*from xxxx where id=1 for update;悲观锁使用时一般伴随事务一起使用,数据锁定时间可能会很长,需要根据实际情况选用。另外要注意的是,id字段一定是主键或者唯一索引,不然可能造成锁表的结果,处理起来会非常麻烦。2、数据库乐观锁这种方法适合在更新的场景中update t_goods set count=count-1,version =version+1 where good_id=2 and version=1根据 version版本,也就是在操作库存前先获取当前商品的 version 版本号,然后操作的时候带上此version号。我们梳理下,我们第一次操作库存时,得到version为1,调用库存服务version变成了2;但返回给订单服务出现了问题,订单服务又一次发起调用库存服务,当订单服务传如的version还是1,再执行上面的sql语句时,就不会执行;因为version已经变为2了,where条件就不成立。这样就保证了不管调用几次,只会真正的处理一次。乐观锁主要使用于处理读多写少的问题。3、业务层分怖式锁如果多个机器可能在同一时间同时处理相同的数据,比如多台机器定时任务都拿到了相同数据处理,我们就可以加分布式锁,锁定此数据,处理完成后释放锁。获取到锁的必须先判断这个数据是否被处理过。3、各种唯一约束1、数据库唯一约束插入数据,应该按照唯一索引进行插入,比如订单号,相同的订单就不可能有两条记录插入。我们在数据库层面防止重复。这个机制是利用了数据库的主键唯一约束的特性,解决了在insert场景时幂等问题。但主键的要求不是自增的主键,这样就需要业务生成全局唯一的主键。如果是分库分表场景下,路由规则要保证相同请求下,落地在同一个数据库和同一表中,要不然数据库主键约束就不起效果了,因为是不同的数据库和表主键不相关。2、redis set防重很多数据需要处理,只能被处理一次,比如我们可以计算数据的MD5将其放入redis的set,每次处理数据,先看这个MD5是否已经存在,存在就不处理。4、防重表使用订单号orderNo做为去重表的唯一索引,把唯一索引插入去重表,再进行业务操作,且他们在同一个事务中。这个保证了重复请求时,因为去重表有唯一约束,导致请求失败,避免了幂等问题。这里要注意的是,去重表和业务表应该在同一库中,这样就保证了在同一个事务,即使业务操作失败了,也会把去重表的数据回滚。这个很好的保证了数据一致性。redis防重也算。5、全局请求唯一id调用接口时,生成一个唯一id,redis 将数据保存到集合中(去重),存在即处理过。可以使用nginx设置每一个请求的唯一id;proxy_set_header X-Request-ld Srequest_id;
2022年03月11日
271 阅读
0 评论
10 点赞
2022-03-11
初识k8s
初识k8s简介:Kubernetes 简称k8s。是用于自动部署,扩展和管理容器化应用程序的开源系统。中文官网:https://kubernetes.io/zh/中文社区:https://www.kubernetes.org.cn/官方文档:https://kubernetes.io/zh/docs/home/社区文档:http://docs.kubernetes.org.cn/部署方式的进化https://kubernetes.io/zh/docs/concepts/overview/功能架构整体主从方式Master 节点架构kube-apiserver对外暴露K8S 的api 接口,是外界进行资源操作的唯一入口。提供认证、授权、访问控制、API 注册和发现等机制。etcdetcd 是兼具一致性和高可用性的键值数据库,可以作为保存Kubernetes 所有集群数据的后台数据库。Kubernetes 集群的etcd 数据库通常需要有个备份计划kube-scheduler主节点上的组件,该组件监视那些新创建的未指定运行节点的Pod,并选择节点让Pod 在上面运行。所有对k8s 的集群操作,都必须经过主节点进行调度kube-controller-manager在主节点上运行控制器的组件这些控制器包括:节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应。副本控制器(Replication Controller): 负责为系统中的每个副本控制器对象维护正确数量的Pod。端点控制器(Endpoints Controller): 填充端点(Endpoints)对象(即加入Service与Pod)。服务帐户和令牌控制器(Service Account & Token Controllers): 为新的命名空间创建默认帐户和API 访问令牌Node 节点架构kubelet一个在集群中每个节点上运行的代理。它保证容器都运行在Pod 中。负责维护容器的生命周期,同时也负责Volume(CSI)和网络(CNI)的管理;kube-proxy负责为Service 提供cluster 内部的服务发现和负载均衡;容器运行环境(Container Runtime)容器运行环境是负责运行容器的软件。Kubernetes 支持多个容器运行环境: Docker、containerd、cri-o、rktlet 以及任何实现Kubernetes CRI (容器运行环境接口)。fluentd是一个守护进程,它有助于提供集群层面日志集群层面的日志概念Container:容器,可以是docker 启动的一个容器Pod:k8s 使用Pod 来组织一组容器一个Pod 中的所有容器共享同一网络。Pod 是k8s 中的最小部署单元Volume声明在Pod 容器中可访问的文件目录可以被挂载在Pod 中一个或多个容器指定路径下支持多种后端存储抽象(本地存储,分布式存储,云存储…)Controllers:更高层次对象,部署和管理Pod;ReplicaSet:确保预期的Pod 副本数量Deplotment:无状态应用部署StatefulSet:有状态应用部署DaemonSet:确保所有Node 都运行一个指定PodJob:一次性任务Cronjob:定时任务Deployment:定义一组Pod 的副本数目、版本等通过控制器(Controller)维持Pod 数目(自动回复失败的Pod)通过控制器以指定的策略控制版本(滚动升级,回滚等)Service定义一组Pod 的访问策略Pod 的负载均衡,提供一个或者多个Pod 的稳定访问地址支持多种方式(ClusterIP、NodePort、LoadBalance)Label:标签,用于对象资源的查询,筛选Namespace:命名空间,逻辑隔离一个集群内部的逻辑隔离机制(鉴权,资源)每个资源都属于一个namespace同一个namespace 所有资源名不能重复不同namespace 可以资源名重复API我们通过kubernetes 的API 来操作整个集群。可以通过kubectl、ui、curl 最终发送http+json/yaml 方式的请求给API Server,然后控制k8s集群。注意k8s 里的所有的资源对象都可以采用yaml 或JSON 格式的文件定义或描述流程叙述1、通过Kubectl 提交一个创建RC(Replication Controller)的请求,该请求通过APIServer被写入etcd 中2、此时Controller Manager 通过API Server 的监听资源变化的接口监听到此RC 事件3、分析之后,发现当前集群中还没有它所对应的Pod 实例,4、于是根据RC 里的Pod 模板定义生成一个Pod 对象,通过APIServer 写入etcd5、此事件被Scheduler 发现,它立即执行一个复杂的调度流程,为这个新Pod 选定一个落户的Node,然后通过API Server 讲这一结果写入到etcd 中,6、目标Node 上运行的Kubelet 进程通过APIServer 监测到这个“新生的”Pod,并按照它的定义,启动该Pod 并任劳任怨地负责它的下半生,直到Pod 的生命结束。7、随后,我们通过Kubectl 提交一个新的映射到该Pod 的Service 的创建请求8、ControllerManager 通过Label 标签查询到关联的Pod 实例,然后生成Service 的Endpoints 信息,并通过APIServer 写入到etcd 中,9、接下来,所有Node 上运行的Proxy 进程通过APIServer 查询并监听Service 对象与其对应的Endpoints 信息,建立一个软件方式的负载均衡器来实现Service 访问到后端Pod 的流量转发功能。k8s 里的所有的资源对象都可以采用yaml 或JSON 格式的文件定义或描述
2022年03月11日
427 阅读
0 评论
6 点赞
2022-03-11
CompletableFuture异步编排
通过线程池性能稳定,也可以获取执行结果,并捕获异常。但是,在业务复杂情况下,一个异步调用可能会依赖于另一个异步调用的执行结果。因此我们可以使用completableFuture 异步编排方案。比如:一个业务场景,需要同时获取多个数据,如果同步线程挨个执行,则需要时间为所有线程执行时间的总和。如果我们使用异步线程执行,所需时间则为耗时最长那个异步线程的执行时间。如果多个异常线程之间还存在依赖关系,比如线程3需要线程1的执行结果,线程6依赖线程3、线程2,那这个问题怎么解决呢。那就可以使用completableFuture 异步编排方案实现。注意:completableFuture 是jdk1.8之后添加的一个功能。CompletableFuture接口:public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {public interface Future<V> {以前用到的FutureTask就是用到的Future可以得到返回结果public class FutureTask<V> implements RunnableFuture<V> {public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); }Future可以得到返回结果CompletableFuture随便一个方法,都接受一个Function public <U> CompletableFuture<U> applyToEither( CompletionStage<? extends T> other, Function<? super T, U> fn) { return orApplyStage(null, other, fn); }@FunctionalInterface public interface Function<T, R> {Function是一个@FunctionalInterface,所以对Lambda使用要熟悉。CompletableFuture异步编排问题多个异步线程远程调用会导致丢失请求头,原因是多个线程,通过拦截器不能获取其它线程的请求的heard信息。解决方案:每个线程共享自己的requestAttributes。自定义feign拦截器:import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * @description: TODO * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 23:03 * @version: 1.0 */ @Configuration public class FamilyFegin { @Bean(name ="requestInterceptor") public RequestInterceptor requestInterceptor(){ return new RequestInterceptor() { @Override public void apply(RequestTemplate requestTemplate) { //RequestContextHolder拿到刚请求来的数据 ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); System.out.println("requestAttributes线程"+Thread.currentThread().getId()); HttpServletRequest request = requestAttributes.getRequest(); System.out.println("调用feign之前"); if(request != null){ //同步请求头数据 String cookie = request.getHeader("Cookie");//老数据 //给新请求同步老请求的cookie requestTemplate.header("Cookie", cookie); } } }; } }web配置:因为需要拦截器生效import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @description: WEB配置 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 22:40 * @version: 1.0 */ public class FamilyWebConfig implements WebMvcConfigurer { @Autowired LoginUserInterceptor loginUserInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**"); } }CompletableFuture异步编排import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @description: CompletableFuture异步编排 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 23:32 * @version: 1.0 */ public class MyCompletableFuture { public static ExecutorService executorService = Executors.newFixedThreadPool(10); public void myOpenFeign() throws ExecutionException, InterruptedException { System.out.println("主线程0"); //远程调用存在丢失请求头的问题,因为不在同一线程,导致自定义拦截器不能获取head信息。 //解决丢失请求头方案: ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); //第一个异步任务 CompletableFuture<Void> oneFuture = CompletableFuture.runAsync(() -> { System.out.println("辅线程1"); //每个线程都共享一下自己的requestAttributes RequestContextHolder.setRequestAttributes(requestAttributes); //异步线程远程调用,业务1 //异步线程调用业务代码 }, executorService); //第二个异步任务 CompletableFuture<Void> twoFuture = CompletableFuture.runAsync(() -> { System.out.println("辅线程2"); //每个线程都共享一下自己的requestAttributes RequestContextHolder.setRequestAttributes(requestAttributes); //异步线程远程调用,业务2 //异步线程调用业务代码 }, executorService); CompletableFuture.allOf(oneFuture, twoFuture).get(); } }以上就是解决CompletableFuture异步编排,异步多线程引起的远程调用请求丢失解决方案。代码中共享requestAttributes原因ThreadLocal数据:ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();RequestContextHolder主线程不一样获取的数据不一样,如下:import javax.faces.context.FacesContext; import org.springframework.core.NamedInheritableThreadLocal; import org.springframework.core.NamedThreadLocal; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; public abstract class RequestContextHolder { private static final boolean jsfPresent = ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader()); private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes"); private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder = new NamedInheritableThreadLocal("Request context"); public RequestContextHolder() { }
2022年03月11日
502 阅读
0 评论
5 点赞
2022-03-10
Feign远程调用丢失请求头问题
openFeign远程调用丢失请求头feign在远程调用之前要构造请求,调用会经过很多拦截器。丢失请求头原因,是远程调用重新构建了request请求,新的request没有请求头headr。解决方案:定义一个feign的拦截器先来看一下feign远程调用类SynchronousMethodHandler// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package feign; import feign.InvocationHandlerFactory.MethodHandler; import feign.Logger.Level; import feign.Request.Options; import feign.codec.Decoder; import feign.codec.ErrorDecoder; import java.io.IOException; import java.util.Iterator; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; final class SynchronousMethodHandler implements MethodHandler { private static final long MAX_RESPONSE_BUFFER_SIZE = 8192L; private final MethodMetadata metadata; private final Target<?> target; private final Client client; private final Retryer retryer; private final List<RequestInterceptor> requestInterceptors; private final Logger logger; private final Level logLevel; private final feign.RequestTemplate.Factory buildTemplateFromArgs; private final Options options; private final ExceptionPropagationPolicy propagationPolicy; private final Decoder decoder; private final AsyncResponseHandler asyncResponseHandler; private SynchronousMethodHandler(Target<?> target, Client client, Retryer retryer, List<RequestInterceptor> requestInterceptors, Logger logger, Level logLevel, MethodMetadata metadata, feign.RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder, boolean decode404, boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy, boolean forceDecoding) { this.target = (Target)Util.checkNotNull(target, "target", new Object[0]); this.client = (Client)Util.checkNotNull(client, "client for %s", new Object[]{target}); this.retryer = (Retryer)Util.checkNotNull(retryer, "retryer for %s", new Object[]{target}); this.requestInterceptors = (List)Util.checkNotNull(requestInterceptors, "requestInterceptors for %s", new Object[]{target}); this.logger = (Logger)Util.checkNotNull(logger, "logger for %s", new Object[]{target}); this.logLevel = (Level)Util.checkNotNull(logLevel, "logLevel for %s", new Object[]{target}); this.metadata = (MethodMetadata)Util.checkNotNull(metadata, "metadata for %s", new Object[]{target}); this.buildTemplateFromArgs = (feign.RequestTemplate.Factory)Util.checkNotNull(buildTemplateFromArgs, "metadata for %s", new Object[]{target}); this.options = (Options)Util.checkNotNull(options, "options for %s", new Object[]{target}); this.propagationPolicy = propagationPolicy; if (forceDecoding) { this.decoder = decoder; this.asyncResponseHandler = null; } else { this.decoder = null; this.asyncResponseHandler = new AsyncResponseHandler(logLevel, logger, decoder, errorDecoder, decode404, closeAfterDecode); } } public Object invoke(Object[] argv) throws Throwable { RequestTemplate template = this.buildTemplateFromArgs.create(argv); Options options = this.findOptions(argv); Retryer retryer = this.retryer.clone(); while(true) { try { return this.executeAndDecode(template, options); } catch (RetryableException var9) { RetryableException e = var9; try { retryer.continueOrPropagate(e); } catch (RetryableException var8) { Throwable cause = var8.getCause(); if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) { throw cause; } throw var8; } if (this.logLevel != Level.NONE) { this.logger.logRetry(this.metadata.configKey(), this.logLevel); } } } } Object executeAndDecode(RequestTemplate template, Options options) throws Throwable { Request request = this.targetRequest(template); if (this.logLevel != Level.NONE) { this.logger.logRequest(this.metadata.configKey(), this.logLevel, request); } long start = System.nanoTime(); Response response; try { response = this.client.execute(request, options); response = response.toBuilder().request(request).requestTemplate(template).build(); } catch (IOException var12) { if (this.logLevel != Level.NONE) { this.logger.logIOException(this.metadata.configKey(), this.logLevel, var12, this.elapsedTime(start)); } throw FeignException.errorExecuting(request, var12); } long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); if (this.decoder != null) { return this.decoder.decode(response, this.metadata.returnType()); } else { CompletableFuture<Object> resultFuture = new CompletableFuture(); this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime); try { if (!resultFuture.isDone()) { throw new IllegalStateException("Response handling not done"); } else { return resultFuture.join(); } } catch (CompletionException var13) { Throwable cause = var13.getCause(); if (cause != null) { throw cause; } else { throw var13; } } } } long elapsedTime(long start) { return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start); } Request targetRequest(RequestTemplate template) { Iterator var2 = this.requestInterceptors.iterator(); while(var2.hasNext()) { RequestInterceptor interceptor = (RequestInterceptor)var2.next(); interceptor.apply(template); } return this.target.apply(template); } Options findOptions(Object[] argv) { if (argv != null && argv.length != 0) { Stream var10000 = Stream.of(argv); Options.class.getClass(); var10000 = var10000.filter(Options.class::isInstance); Options.class.getClass(); return (Options)var10000.map(Options.class::cast).findFirst().orElse(this.options); } else { return this.options; } } static class Factory { private final Client client; private final Retryer retryer; private final List<RequestInterceptor> requestInterceptors; private final Logger logger; private final Level logLevel; private final boolean decode404; private final boolean closeAfterDecode; private final ExceptionPropagationPolicy propagationPolicy; private final boolean forceDecoding; Factory(Client client, Retryer retryer, List<RequestInterceptor> requestInterceptors, Logger logger, Level logLevel, boolean decode404, boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy, boolean forceDecoding) { this.client = (Client)Util.checkNotNull(client, "client", new Object[0]); this.retryer = (Retryer)Util.checkNotNull(retryer, "retryer", new Object[0]); this.requestInterceptors = (List)Util.checkNotNull(requestInterceptors, "requestInterceptors", new Object[0]); this.logger = (Logger)Util.checkNotNull(logger, "logger", new Object[0]); this.logLevel = (Level)Util.checkNotNull(logLevel, "logLevel", new Object[0]); this.decode404 = decode404; this.closeAfterDecode = closeAfterDecode; this.propagationPolicy = propagationPolicy; this.forceDecoding = forceDecoding; } public MethodHandler create(Target<?> target, MethodMetadata md, feign.RequestTemplate.Factory buildTemplateFromArgs, Options options, Decoder decoder, ErrorDecoder errorDecoder) { return new SynchronousMethodHandler(target, this.client, this.retryer, this.requestInterceptors, this.logger, this.logLevel, md, buildTemplateFromArgs, options, decoder, errorDecoder, this.decode404, this.closeAfterDecode, this.propagationPolicy, this.forceDecoding); } } } 远程调用时,构建了RequestInterceptor。自定义feign拦截器import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; /** * @description: TODO * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 23:03 * @version: 1.0 */ @Configuration public class FamilyFegin { @Bean(name ="requestInterceptor") public RequestInterceptor requestInterceptor(){ return new RequestInterceptor() { @Override public void apply(RequestTemplate requestTemplate) { //RequestContextHolder拿到刚请求来的数据 ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); System.out.println("调用feign之前"); //同步请求头数据 String cookie = request.getHeader("Cookie");//老数据 //给新请求同步老请求的cookie requestTemplate.header("Cookie", cookie); } }; } }拦截器说明RequestContextHolder.getRequestAttributes()获取到RequestAttributes类,RequestAttributes源码:// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.web.context.request; import org.springframework.lang.Nullable; public interface RequestAttributes { int SCOPE_REQUEST = 0; int SCOPE_SESSION = 1; String REFERENCE_REQUEST = "request"; String REFERENCE_SESSION = "session"; @Nullable Object getAttribute(String var1, int var2); void setAttribute(String var1, Object var2, int var3); void removeAttribute(String var1, int var2); String[] getAttributeNames(int var1); void registerDestructionCallback(String var1, Runnable var2, int var3); @Nullable Object resolveReference(String var1); String getSessionId(); Object getSessionMutex(); } ServletRequestAttributes继承了AbstractRequestAttributes,AbstractRequestAttributes实现了RequestAttributes,因此可以强转。 public abstract class AbstractRequestAttributes implements RequestAttributes { protected final Map<String, Runnable> requestDestructionCallbacks = new LinkedHashMap(8); private volatile boolean requestActive = true; public AbstractRequestAttributes() { } public void requestCompleted() {注意:该处理是在同步一个线程种处理的,才能获取之前的heard中的cookie信息。异步多线程该处理方式不同。
2022年03月10日
380 阅读
0 评论
6 点赞
2022-03-10
Springboot拦截器配置
Springboot拦截器配置1、拦截器配置,主要实现HandlerInterceptor接口import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @description: 拦截器,配合FamilyWebConfig 配置使用 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 22:36 * @version: 1.0 */ @Component public class LoginUserInterceptor implements HandlerInterceptor { public static ThreadLocal<Object> objUser = new ThreadLocal<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginusr = request.getSession().getAttribute("LOGIN_USR"); if(loginusr != null){ objUser.set(loginusr); //登录了才能访问 return true; }else{ //没登录,跳转去登录 return false; } } } 由于拦截器需要配合web配置使用,因此创建web配置。2、web配置主要实现WebMvcConfigurer接口import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @description: WEB配置 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 22:40 * @version: 1.0 */ public class FamilyWebConfig implements WebMvcConfigurer { @Autowired LoginUserInterceptor loginUserInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**"); } }
2022年03月10日
300 阅读
0 评论
2 点赞
2022-03-10
JAVA多线程4种实现方式
异步与线程池1、初始化线程的4种方式1)、继承Thread2)、实现Runnable接口3)、实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)4)、线程池方式1和方式2:主进程无法获取线程的运算结果。不适合当前场景。方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。方式4:通过如下两种方式初始化线程池Executors.newFiexed ThreadPool(3); //或者 new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit unit, workQueue,threadFactory,handler);方式1、继续Thread/** * @description: 继承Thread * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { public static void main(String[] args) { System.out.println("启动main方法开始"); OneMyThread oneMyThread = new OneMyThread(); oneMyThread.start(); System.out.println("结束main方法"); } public static class OneMyThread extends Thread{ @Override public void run() { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); } } }运行结果:启动main方法开始 结束main方法 当前线程:22 运行结果:5方式2、实现Runable接口/** * @description: 实现Runnable接口 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { public static void main(String[] args) { System.out.println("启动main方法开始"); TwoMyThread twoMyThread = new TwoMyThread(); new Thread(twoMyThread).start(); System.out.println("结束main方法"); } /** * 实现Runnable接口 */ public static class TwoMyThread implements Runnable { @Override public void run() { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); } } }运行结果:启动main方法开始 结束main方法 当前线程:22 运行结果:5方式3、实现Callable泛型接口/** * @description: 继承Thread * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("启动main方法开始"); FutureTask<Integer> futureTask = new FutureTask<>(new ThreeMyThread()); new Thread(futureTask).start(); //等待线程执行完成,返回结果 Integer i = futureTask.get(); System.out.println("线程执行完,返回结果:"+i); System.out.println("结束main方法"); } /** * 实现Callable<T>泛型接口 */ public static class ThreeMyThread implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); return i; } } }运行结果:启动main方法开始 当前线程:22 运行结果:5 线程执行完,返回结果:5 结束main方法注意:结果的顺序,可以看到是一个阻塞等待通过FutureTask类源码可以看到,FutureTask不仅可以接受Callable还可以接收Runnable.FutureTask源码如下:public class FutureTask<V> implements RunnableFuture<V> { /** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Runnable}, and arrange that {@code get} will return the * given result on successful completion. * * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: * {@code Future<?> f = new FutureTask<Void>(runnable, null)} * @throws NullPointerException if the runnable is null */ public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }FutureTask实现RunnableFuture接口:RunnableFuture最终是继承的Runnable。package java.util.concurrent; /** * A {@link Future} that is {@link Runnable}. Successful execution of * the {@code run} method causes completion of the {@code Future} * and allows access to its results. * @see FutureTask * @see Executor * @since 1.6 * @author Doug Lea * @param <V> The result type returned by this Future's {@code get} method */ public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); }方式4、直接提交线程池,线程池会自动开启任务。每次通过new Thread()创建线程问题:导致资源耗尽。以上三种启动方式都不用。应该将所有异步多线程任务交给线程池执行。线程池:package com.yanxizhu.family.booking; import java.util.concurrent.*; /** * @description: 继承Thread * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { //应保证,当前系统中线程池只有一两个,每个异步任务,提交给线程池自己执行。 public static ExecutorService executorService = Executors.newFixedThreadPool(10); public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("启动main方法开始"); executorService.submit(new TwoMyThread()); //有返回值 //每个异步任务,提交给线程池自己执行。 executorService.execute(new TwoMyThread());//无返回值 System.out.println("结束main方法"); } /** * 实现Runnable接口 */ public static class TwoMyThread implements Runnable { @Override public void run() { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); } } } 运行结果:启动main方法开始 结束main方法 当前线程:22 运行结果:5使用线程池带来的好处:1、降低资源的消耗通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗2、提高响应速度因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行3、提高线程的可管理性线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配总结区别1、直接继承Thread、实现Runnable接口:不能直接得到返回值。2、实现Callable泛型接口:可以得到返回值。3、继承Thread、实现Runnable接口、实现Callable泛型接口,都不能达到控制资源的效果。4、只有线程池可以控制资源,性能稳定。
2022年03月10日
264 阅读
0 评论
1 点赞
2022-03-10
Springboot整合SpringSession,解决分布式session共享问题
springboot整合SpringSession1、引入依赖<dependencies> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> </dependencies>2、springboot配置application.properties配置:保存类型spring.session.store-type=redis # Session store type.session过期时间:server.servlet.session.timeout= # Session timeout. If a duration suffix is not specified, seconds is used.其它配置信息可参考官网:spring.session.redis.flush-mode=on_save # Sessions flush mode. spring.session.redis.namespace=spring:session # Namespace for keys used to store sessions.连接信息配置:spring.redis.host=localhost # Redis server host. spring.redis.password= # Login password of the redis server. spring.redis.port=6379 # Redis server port.3、开启springSession主类加上注解@EnableRedisHttpSession@EnableRedisHttpSession4、使用注意:1、需要保存到redis中的数据必须序列化。2、需要实现session共享的微服务都必须整合SpringSession。5、解决子域session共享、cookie进行json序列化MySessionConfig配置package com.yanxizhu.family.booking.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.session.web.http.CookieSerializer; import org.springframework.session.web.http.DefaultCookieSerializer; /** * @description: springSession配置 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 20:11 * @version: 1.0 */ @Configuration public class MySessionConfig { @Bean public CookieSerializer cookieSerializer(){ DefaultCookieSerializer defaultCookieSerializer = new DefaultCookieSerializer(); //设置共享域名 defaultCookieSerializer.setDomainName("yanxizhu.com"); //设置session名称 defaultCookieSerializer.setCookieName("YANXIZHUSESSION"); return defaultCookieSerializer; } //session序列化机制 @Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer(){ return new GenericJackson2JsonRedisSerializer(); } }注意:需要session共享的微服务都需要配置,或者抽取到公共模块。6、多系统-session共享问题(单点登录)抽取单独的认证中心微服务处理单点登录单点登录,可参考一个开源单点登录项目,许雪里/xxl-sso
2022年03月10日
460 阅读
0 评论
7 点赞
2022-03-08
ES安装ik分词器
注意:不能用默认elasticsearch-plugin install xxx.zip 进行自动安装 https://github.com/medcl/elasticsearch-analysis-ik/releases?after=v6.4.2 对应es 版本安装。1、进入es 容器内部plugins 目录docker exec -it 容器id /bin/bash2、下载wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-anal ysis-ik-7.4.2.zip注意自己的es版本。3、解压unzip 下载的文件4、删除下载的压缩包rm –rf *.zip5、安装mv elasticsearch/ ik6、确认是否安装好了分词器进入文件夹cd ../bin查看系统的分词器mv elasticsearch/ ik
2022年03月08日
278 阅读
0 评论
2 点赞
2022-03-08
JAVA面向对象有那些特征
JAVA面向对象有那些特征?面向对象编程是利用类和对象编程的一种思想。万物可归类,类是对于世界事物的高度抽象,不同的事物之间有不同的关系,一个类自身与外界的封装关系,一个父类和子类的继承关系,一个类和多个类的多态关系。万物皆对象,对象是具体的世界事物,面向对象的三大特征封装,继承,多态。封装,封装说明一个类行为和属性与其他类的关系,低耦合,高内聚;继承是父类和子类的关系,多态说的是类与类的关系。封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。属性的封装:使用者只能通过事先定制好的方法来访问数据,可以方便地加入逻辑控制,限制对属性的不合理操作;方法的封装:使用者按照既定的方式调用方法,不必关心方法的内部实现,便于使用;便于修改,增强代码的可维护性;继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。在本质上是特殊~一般的关系,即常说的is-a关系。子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法。从多种实现类中抽象出一个基类,使其具备多种实现类的共同特性,当实现类用extends关键字继承了基类(父类)后,实现类就具备了这些相同的属性。继承的类叫做子类(派生类或者超类),被继承的类叫做父类(或者基类)。比如从猫类、狗类、虎类中可以抽象出一个动物类,具有和猫、狗、虎类的共同特性(吃、跑、叫等)。Java通过extends关键字来实现继承,父类中通过private定义的变量和方法不会被继承,不能在子类中直接操作父类通过private定义的变量以及方法。继承避免了对一般类和特殊类之间共同特征进行的重复描述,通过继承可以清晰地表达每一项共同特征所适应的概念范围,在一般类中定义的属性和操作适应于这个类本身以及它以下的每一层特殊类的全部对象。运用继承原则使得系统模型比较简练也比较清晰。相比于封装和继承,Java多态是三大特性中比较难的一个,封装和继承最后归结于多态,多态指的是类和类的关系,两个类由继承关系,存在有方法的重写,故而可以在调用时有父类引用指向子类对象。多态必备三个要素:继承,重写,父类引用指向子类对象。继承、封装:增强了代码的可复用性,多态:增加了可移植性、健壮性、灵活性。
2022年03月08日
262 阅读
0 评论
1 点赞
1
...
13
14
15
...
21