Code Monkey home page Code Monkey logo

kischn.github.io's People

Contributors

kischn avatar

Watchers

 avatar

kischn.github.io's Issues

同步与异步

同步与异步

最近用 Java8 里面的 forEach 语法写业务的时候遇到了一个问题,forEach 里面有一个异常,却根本没阻止剩余代码的调用,不由得想起来这异步与同步的区别来了。

同步

Java Web 领域最成功的两个规范是 servlet 和 jdbc 规范,这两个规范建立起来的巨大的生态是其它语言无法比拟的,那同步的核心概念是什么呢?在我看来就是阻塞基于线程的上下文还有异常传递

阻塞

这个就非常容易理解了,调用一个方法的时候在方法返回前该调用会一直阻塞,干不了其它的活。而且整个调用链就是经典的入栈出栈操作,搜一下 CPU 火焰图基本上就可以非常直观得看出来调用栈的概念了,或者是通过以前我们做过一个道括号匹配的算法题来看一下:
()((()()))()

基于线程的上下文

这个又是我自己取的名字,这个有挺多的应用场景的,比如 A -> B -> C 这样的调用,A 想不通过 B 来额外给 C 传递一个参数该怎么样做?其中一个答案是 ThreadLocal,它是依赖当前线程下的一个 Map 对象,只要是当前线程去执行的方法都会访问到同一个 Map,像前面讲的线程会首先调用 A 然后 A 在 ThreadLocal 里面 put 一个值,再执行 B 再到 C 的时候从 ThreadLocal 里面再拿出来就可以了。

import java.util.Random;

public class ThreadLocalTest {

    final ThreadLocal<String> threadLocal = new ThreadLocal<>();

    private void c() {
        System.out.println(Thread.currentThread().getId() + " c get:" + threadLocal.get());
    }

    private void b() {
        System.out.println(Thread.currentThread().getId() + " b invoke");
        try {
            Thread.sleep(new Random().nextInt(100));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        c();
    }

    private void a() {
        final String remark = "threadId" + Thread.currentThread().getId();
        System.out.println(Thread.currentThread().getId() + " a invoke and put:" + remark);
        threadLocal.set(remark);
        b();
    }

    public static void main(String[] args) {
        final Thread thread1 = new Thread(() -> new ThreadLocalTest().a());
        final Thread thread2 = new Thread(() -> new ThreadLocalTest().a());
        thread1.start();
        thread2.start();
    }
}
/*
打印结果样例如下(每次会有差异,但是每个线程打印的都是自己线程的id绝对不会串)
13 a invoke and put:threadId13
14 a invoke and put:threadId14
13 b invoke
14 b invoke
14 c get:threadId14
13 c get:threadId13
*/

这个东西会用在哪里呢?其中一个例子就是 session 相关的处理,在最外层的 Controller 或者 Filter 里面拦截 request 拿到 Authorization 数据并进行验证和相关数据的补充(比如补充上当前登录的用户、部门相关的信息)然后就存到 ThreadLocal 里面,在后面的业务中就可以直接或间接来使用 ThreadLocal 来拿到这个 session 信息了。我现在的项目里面就是封装了一个 JwtUtil 里面有一个 ThreadLocal 实例,首先在 Filter 层校验和处理 jwt 信息然后再补充上用户的真实姓名和部门等信息后存到 ThreadLocal 里面,其它业务模块想登录当前用户相关信息的时候直接 JwtUtil.getJwtInfo() 就可以拿到了。

异常传递

JVM 里面的异常就是一个调用的提前退出,受影响的是整个当前线程的调用栈,所以基于 servlet 模型的编程如果遇到异常会直接报 500,如果你没有 catch 的话, 那假如你 catch 了那肯定就跳到了 catch 里面了。基于此你可以实现什么功能?事务

jdbc 里面你如果设定 autocommit 那如果你的业务需要执行 3 条操作的话其实就是每一条都是一个单独的事务。spring 里面你添加了 @Transactional 注解的话,Spring 就会利用 AOP 的能力,在方法执行前加一个 try/catch 在try开始时通过 jdbc 的协议 set autocommit=0 然后再执行对应的方法,如果正常走则 try 末尾 (不是 finally) 提交一个 commit就可以了,如果程序运行到了 catch 里面,则 rollback 这就是 Spring 的事务了吧。仔细想,这个 jdbc 的协议其实也是一个有状态的协议,它并没有返回给你 txid 让你自己去控制,你 commit/rollback 的命令没有传任何表示当前事务的标记。
从这里也可以看出来,java 的江山,是同步的江山,难改,要改就要把这两个协议都改掉。

拿以前设置的那个 jdbc log 打印的来印证一下
2020-10-10 22:48:17,544 - [INFO] c.s.z.c.j.SogProfilerEventHandler - duration:0,sql:SET autocommit=1
2020-10-10 22:48:17,545 - [INFO] c.s.z.c.j.SogProfilerEventHandler - duration:0,sql:SET sql_mode='NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES'

异常的传递写这一篇文章的最初问题所在,在 多线程 + 异步 的情况下,异常的传递也会是个问题,异常只会跳出当前执行的线程的调用堆栈,跨线程的就无能为力了。Java8 里面 Stream 的 forEach 方法是通过 Task 来实现的,参照 java.util.stream.ForEachOps.ForEachOp#evaluateParallel 方法看一下。

        @Override
        public <S> Void evaluateParallel(PipelineHelper<T> helper,
                                         Spliterator<S> spliterator) {
            if (ordered)
                new ForEachOrderedTask<>(helper, spliterator, this).invoke();
            else
                new ForEachTask<>(helper, spliterator, helper.wrapSink(this)).invoke();
            return null;
        }

js 不会出这样的问题,因为它只有一个线程。 -。-

异步

没怎么写过异步的接口,所以这块也没啥好写的。。就写写异步编程的情况下该如何实现上面的那个 session 问题和事务问题吧。

异步下的 session

vert.x 这样的 JVM 上的异步编程库,请求数据肯定不能和线程来绑定了,因为一个线程可能会同时处理多个请求,而且整个请求也会有多个线程来处理。在这样的处理过程中没有什么东西是可以从头带到尾的,没有地方可以存状态。。前期可以通过 request 对象来承载一些额外的数据,但是后期只能是自己生成一个 key 来相关穿插传递了,其中 key 可以直接是数据,也可以是 redis 的一个 key 后期再去 redis 取。

异步下的事务

其实同 session 一样,也是没有什么地方可以保存状态,最理想的情况就是 jdbc 协议在 begin transaction 的时候返回一个 txid 然后后续的任何操作都要带上这个 txid 直到最后所有操作都完成,再 commit txid 把这个事务提交。

如果还是沿用同步的 jdbc 协议的话,前面的异步到了 sql 执行这一步还是要必须由同一个线程+同一个连接来执行所有的相关SQL操作才能保证事务一致,是不是一听就挺麻烦的。。

最后

写同步的程序比异步的其实简单很多。。是不是?

最近写项目接口的感想

最近写接口文档的感想

起因

最近在一个新项目的开发,按照当前公司里面的开发流程来说,首先是定接口。花了一整天才在 rap2 上写完自己手头的那部分功能的接口文档,先不说这个接口需要明确由前端或后端还是前后端坐一块讨论的问题,只是看到自己用了一天写出来的仅仅是一堆接口描述就挺失落的: 效率实在是太低了。

深度使用了这个 rap2 一整天,感觉就是这个东西真的有点难堪大用,先说一个最有痛点的地方就是,很多接口里面的返回数据格式都是相同的,而 rap2 里面并没有提供这种可以描述 “相同” 的相关功能,我只能自己去一个一个的添加,或者把接口复制一遍,写也就算了,万一后面修改,还要自己一个一个的去翻,真是酸爽。

思考

一个活干到怀疑人生的时候,首先要怀疑一下自己是不是可以不干这个事,所以我就想如果不用 rap2 我该怎样来描述这一些接口呢?

接口的意义

在想办法描述清楚一件事情之前,首先是要搞清楚这件事情的意义嘛。

前后端分离式的开发也是近几年才开始变得流行,服务器后端不再需要进行页面的渲染,只需要组织好合适的数据交给浏览器端的页面去动态渲染就好了。

从直觉或者是从经验的角度去想,凡是中间多了一层的概念,你就需要多花额外的精力去实现原来并不怎么难的功能。比如你可以直接在 controller 中调用 jdbc 查询直接返回,后来有了 entity 后你就需要考虑一下 entity 都需要哪些属性才能最大程度上的复用,再后来提取了 dao 你就需要抽象一些相关的接口,再后来再抽出 service 层的话工作量就会再上升一个层次。这也是为什么在前后端分离之后 php 变得不是那么香的原因了吧。

现在一个现实就是前后端已经分离了,接口规则立刻就变得那么重要了,所以我们就设想一下有哪些新的工作需要我们去完成:

  1. 找一个可以描述接口的媒介,能简单又高效的让前后端都能明白当前定义的接口的含义

  2. 需要好好思考一下接口该如何定以至于能满足当前的业务需求

  3. 前后端需要以定好的接口为准,分头开发;

  4. 如何约束接口与具体实现之间的匹配也是一个问题?

关于以上4点,在 rap2 中解决 1,2,3。第4步 rap2 只解决了前端那一半问题,因为 rap2 支持 mock 可以让前端在开发的时候就请求一些数据。而第1点的实现,我个人觉得有一些不足,前面也说过了。

接口描述工具可以做到什么样

所以,如果让我来的话,我会有什么好的主意呢?

首先,在描述能力方面,我还是受 Java 的影响,觉得一个接口的描述无非就是地址、入参、出参,而带类型的语言描述这种东西非常合适。

首先地址这个,Java 中已经有非常好的示范了,就是 @RequestMapping,有 PATH 有 METHOD,完美。入参这个,定义一个类不就行了?出参也定义一个?假如入参与出参一样复用一下不就行了?再假如多个接口的入参或出参一致,那就再复用一下就行了?其实写到这里经历过的都知道,swagger 就能干这个,但是我觉得 swagger 格局还是略小。

再考虑上面的第4点,关于约束。基于前面那个想法我可以通过 Java 来进行接口描述,我是不是就可以通过解析自己编写的描述代码,去生成前后端的规范呢?前端好说,甚至现在 swagger 就能生成,只不过还是倾向于描述,而不是像 js sdk 那样有一个专门的 js 文件来控制入参与出参。而到了 Java 这边,我是否可以生成一些 interface 等等来进行约定呢?

原来接口描述的发展早就开始了,是我们太落后了

抱着这个想法,我就去网上搜索了一些 api code generate 相关的之后我才了解到,原来开源界已经有 openapi 这样的东西了,排第1的依然是 swagger 的公司在搞的 swagger-codegen,搜到这个之后自己的内心先是有点兴奋后又有一些失落,兴奋就是原来不只是自己有这个想法,失落就是别人想出来的好早,还有周边工具了。

其实并不是多失落了,因为自己的想法和他们还是有一点点不同的,看了一遍 openapi 相关的文档后总结他们的出法点还是更倾向于前端,build 出 sdk 后前端就像调函数接口一样调而不再需要关心 http 啊,地址啊什么的。而我则对这个接口描述有一个更大胆的想法:原来我们的接口只是一个被动生成的东西,我们是不是可以让接口描述反客为主?你想到了没?

网关与接口结合的能量有多大?

假设我们的网关有了接口定制能力,对于前端他们不管是 mock 还是正常上线,都是一直在跟网关打交道。而后端就需要变主动为被动了,对,就是后端需要把自己的接口一个一个注册到网关里面去,网关里面没有的后端注册了就白注册,网关里面有而后端没注册的那就是服务不可用,假设有多个后端对同一个接口注册了多次,那是不是就可以进行负载均衡,如果有接口异常是不是还可以降级?熔断?

照着这个思路想下去,感觉这个东西的格局好像比现有的都很大,比如 spring cloud 则集中在 java 领域,而上面所想的则是包容所有 http 协议的实现,甚至多语言实现也都是没有问题,更甚者,你都可以每一个接口实现都写一套代码,这样的话那就是 AWS 的 lambda 编程?

这样做的一种情况就是网关作为一个核心组件需要承担非常多的功能,在架构方面可以考虑一分为二,一部分就是单纯的网关和接口定义功能,另一部分就是服务发现与服务治理相关。第一部分目前好像还没有出现一个产品,第二部分有一个差不多的理念的东西叫 service mesh,其中目前最出名的实现就是 istio。

所以假如这个想法可行,那 spring cloud 的局面将很快改变?

并没有结束。

关注技术的人肯定也了解过 facebook 首先提出来的 graphicQL,发出来的相关文章都说 graphicQL 怎么怎么好,解决了什么问题,但是没有一篇文章讲他们是如何具体使用 graphicQL 的。 其实上面描述的那一套架构天然支持 graphicQL。你想,关系型数据库里面有了确定的表以后就可以用灵活的 SQL 查询,那现在已经有了确定的 openapi 了,支持个 grapicQL 是不是水到渠成的事?

以后网关是不是大有可为?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.