欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【JMicro】1. JMicro简介

程序员文章站 2024-03-22 17:24:16
...

一,为什么写JMicro

印象中初次接触微服务大概是2011年,那会做Eclpise插件开发,网上查看好多关于OSGI的技术文章,发现Spring新出了一个叫Spring-boot的框架,那会没太上心,只是了解了点皮毛,工作又太忙,之后就没下文了。

直到大概2015年的某天,碰到一个小项目,没什么难度,都用老套路去玩,没什么意思,得玩点新东西才行,也不枉一翻付出,于是选择用GO语言实现,选择GO主要是想体验一下GO,看是不是真如传说中的那样无敌。经过一翻折腾,最终确定GOGIN+GOMICRO实现。是的,从那会开始,通过学心和使用GOMICRO,从此迷上微服务。后来因为工作需要,再没什么机会在项目中接触GO。

后面也曾试图去用Dubbo和Spring-Cloud做项目,但也止于浅尝则止,没能深入。一方面项目时间太紧,折腾不起。另一方面,也是最重要的,项目组成员根本不愿意去学新东西,在很多成员心中,微服务,Spring-Cloud太深奥,玩不起,没时间,至于Dubbo,写个服务,做个RPC也是从百度复制下来的,跑起来就完事了,没人关心个中的原理,经常碰到问题就抓狂!

从那时候起,就一直琢磨着能不能用Java写一个像GoMicro一样简单的微服务框架,这个框架要确保足够简单,入门和使用成本要底,就像写HelloWord一样,但微服务的基本功能要全。因此项目依赖不能多,打出来的运行包也要小,能不用的第三方包坚决不用,最好使用JDK库就行!

二.从RPC开始

微服务的基础是RPC,而反过来单单有RPC却不能叫微服务,微服务是在RPC基础上,实现一系列的基础性功能支持后才能叫微服务。没有超时,限流,熔断这些基础支持,服务“微”了之后,随时都可能挂机,这就是为什么微服务之前存在很多RPC框架,但却没人说那是微服务。

坚持从JDK库入手,使用原生NIO SOCKET做了个简单的RPC,并且兼容了HTTP方式,完后做压力测试发现性能太差,然后退一步吧,选择Mina做底层通信重新再折腾一遍,结果还是不尽如人意,性能还可以,就是自觉得太繁琐臃肿,还是不满意,最终又改为Netty才完成RPC基本功能开发。很多时候,路就是一步一个脚印走出来的,如果前面碰到大山大河,实在跨不过去,回头绕个湾也是必须的,否则一步没跨过去就OVER了。最优的不是选择最好的,而是选择合适的。

接着做IOC,超时,熔断,限速,配置管理,服务注理,监控服务,负载均衡,服务路由,API网关,HTTP,WEB Socket,全局ID,链路追踪,消息服务,异步RPC,基于VUE做的后台管理,服务编排。。。。,到现下图功能基本上都有实现,并且正常可用,后期肯定会不断优化。
【JMicro】1. JMicro简介

实现以上功能的全部第三方依赖如下:

其中

javapoet是编译时做代码生成用的,在最终运行包中并不需要

guava前期实现限流试验性用到,最后限速并不理想,实际上已经排除
【JMicro】1. JMicro简介
Curator后期打算去除,只保留Zookeeper.

三,目前最满意的一个特性:异步RPC

大家都知道,NodeJS单线程,但其性能却很强劲,因为异步;Redis性能也很强劲,因为异步;无数的库或服务都强调通过异步提高性能。

在对JMIcro做压力测试过程中,我深深地体会到一个同步的代价(性能底还不说,高并发时还会死锁)而异步的必要性。在JMicro中,异步体现在很多模块中。其中主要以下几个:

消息服务,一个针对JMicro量身定制的异步消息中间件,服务可以通过其实现消息发布/消息订阅; 还可以做异步RPC,比如发布一个RPC服务方法,使其自动订阅特定主题的消息,消息中间件能识别此种类型的服务方法,并将匹配的消息发送给此服务的服务方法。
以1作为基础,可以在客户端及服务端做异步RPC,比如在客户端直接将消息发送到消息中间件,让消息中间件转发给目标服务;也可以在服务端收到消息后(比如API网关,同步转异步,削峰限流),将消息发送到消息服务器,让消息服务器转发给目标方法。
代码级别的异步RPC调用,让Java代码像Scala,NodeJS等语言一样做异步编程,以下对此做详细的Demo
下面片段代码摘自

https://github.com/mynewworldyyl/jmicro/blob/master/example/example.comsumer/src/main/java/cn/jmicro/example/comsumer/ServiceComsumer.java

ISimpleRpcAsyncClient src = (ISimpleRpcAsyncClient)of.get(ISimpleRpc.class);
         //最外层异步RPC调用
        src.helloAsync("Hello JMicro").then((rst, fail)->{
         //第一次异步返回结果
        System.out.println(rst);
        //做一次同步调用
         String r = src.hello("Hello two");
         //同步返回结果
         System.out.println(r);
          //再做一次异步调用
           src.helloAsync("Hello two").then((rst1, fail1)->{
               //异步返回结果
               System.out.println(rst);
            });
        });

这种异步RPC风格是最有杀伤力的,这使得我们的远程RPC调用不再阻塞线程,包括工作线程和网络IO线程,只要我们的线程占用CPU,他都是在做有用的工作!

四,体验JMiro异步RPC

前面吹了这么多,不来点实际可见的操作,肯定不能让人信服,现在手把手教你体验一把JMicro是不是真的做了远程RPC调用,是不是真的是异步调用。

1. 环境准备

首先确保本机安装了Java,Maven,Zookeeper监听在2181端口,Redis工作在6379端口,保持默认,所有配置都省了。

2. 下载JMicro代码

到https://github.com/mynewworldyyl/jmicro下载代码到特定目录,下面以${basedir}指代此目录

3. 打包注解处理器

打开一个命令行窗口,cd进入到${basedir}\codegenerator目录,执行

mvn clean install -Dmaven.test.skip=true

4. 打包全部依赖

上面命令执行成功后,cd进入到${basedir}目录,执行mvn clean install -Dmaven.test.skip=true

5. 打包样例

确保3和4成功后,cd进入到${basedir}\example目录, 执行mvn clean install-Dmaven.test.skip=true

6. 打包服务提供者

通过3,4,5步确保相关依赖包都已经安装在Maven本地仓库中,打包可执行的服务Jar包,cd进入到${basedir}\example\example.provider目录, 执行 mvn clean install -Pbuild-main -Dmaven.test.skip=true

7. 运行服务端

java -jar target/jmicro-example.provider-0.0.1-SNAPSHOT-jar-with-dependencies.jar -javaagent: ${basedir}\target\jmicro-agent-0.0.1-SNAPSHOT.jar
看到以下输出说明服务启动成功
【JMicro】1. JMicro简介

8. 打包客户端

打包可执行的测试客户端Jar包,cd进入到${basedir}\example\example.comsumer目录, 执行mvn clean install -Pbuild-main -Dmaven.test.skip=true

9. 运行客户端

java -jar target/jmicro-example.comsumer-0.0.1-SNAPSHOT-jar-with-dependencies.jar -javaagent: ${basedir}\target\jmicro-agent-0.0.1-SNAPSHOT.jar
以下是客户端两次异步调用,一次同步调用返回的结果
【JMicro】1. JMicro简介对应服务器的3个被调用服务输出如下
【JMicro】1. JMicro简介
以上样例对应的服务接口及实现类分别为:

https://github.com/mynewworldyyl/jmicro/blob/master/example/example.api/src/main/java/cn/jmicro/example/api/rpc/ISimpleRpc.java

@Service  //指定此接口是一个远程接口,对外提供RPC服务
@AsyncClientProxy //编译时注解,Javac编译器调用特定注解处理器生成客户端访问代码
public interface ISimpleRpc {
    //我们测试用的方法
    String hello(String name);
    //IPromise<String> helloAsync(String name);
    //POJO对象为参数的RPC方法
    String hi(Person p);
    //测试RPC链路
    String linkRpc(String msg);
}

https://github.com/mynewworldyyl/jmicro/blob/master/example/example.provider/src/main/java/cn/jmicro/example/rpc/impl/SimpleRpcImpl.java

服务实现类代码有点多,在此只贴我们调用的方法,别的请查看源代码

public String hello(String name) {
        if(SF.isLoggable(MC.LOG_DEBUG)) {
//向监控服务发送一条日志事件,请放心,调用此方法只是把日志存于本地并立即返回,不影响正常的性能
        SF.eventLog(MC.MT_PLATFORM_LOG,MC.LOG_DEBUG,SimpleRpcImpl.class, name);
        }
        System.out.println("Server hello: " +name);
       //返回一条信息给客户端
        return "Server say hello to: "+name;
}

客户端测试类

https://github.com/mynewworldyyl/jmicro/blob/master/example/example.comsumer/src/main/java/cn/jmicro/example/comsumer/ServiceComsumer.java
完整类图
【JMicro】1. JMicro简介
简化类图
【JMicro】1. JMicro简介

五,实现原理

建议适当修改上面代码然后编译运行测试,比如修改服务端返回值,修改客户端调用参数,服务端返回改为同步,或再调用别的服务(可以同步调用,也可异步调用)。

启动多个example.provider实例(理论上,你可以启动无数个服务实例,那怕都在同一台机器上)多次运行客户端调用看看调用了那个服务实例(服务路由及负载均衡)

如果你对微服务有兴趣,并且有足够的耐心,你应该能从中获得乐趣。

如果认真查看了原代码,应该看到以下几个关键的注解,而使用JMIcro,基本上使用这几个注解就够了:
【JMicro】1. JMicro简介

1. Service

如果注解在接口上,意思是告诉JMicro容器,这是一个服务接实口,客户端使用接口类为参数get实例时,请返回服务代理实例;如果注解在实现类上,告诉JMicro容器,这是一个RPC服务,请将服务信息发布到服务注册中心,让客户端知道我的存在。

2. AsyncClientProxy

注解服务接口,告诉编译器在编译全部源代码前,请调用注解处理器生成服务客户端代理接口及实现类,供客户端直接使用,让客户端觉得自己就好像获得了服务实现类的直接引用一样,完全不知道是跨JVM的远程调用。
【JMicro】1. JMicro简介

3. Component

JMIcro容器组件,JMicro容器启动时实例化,供容器中的其他组件使用,是的,服务本身也是一个组件,只不过其实现了服务接口,并向注册中心注册自己才变成了远程服务,同时其本身也是一个组件,可以被同一个容器中的其他组件依赖并被自动注入。

4. Reference

注解在组件的字段上,告诉JMicro容器,我需要一个远程服务代理实例,请将这个实例值赋给我注解的字段。
【JMicro】1. JMicro简介

5. SMethod

注解在方法上,告诉JMicro容器,我是一个服务方法,并指定服务参数,比如超时时间,熔断策略,是否可监控,日志级别等。

6.服务标识

每个服务都由三个值唯一确定,分别是服务接口全称(serviceName),名称空间(namespace),版本(version),并使用在Service及Reference注解上。

后面有时间会对JMicro的细节做更多介绍,以实战为主,微服务概念为铺。

附一个后台管理的前端链路日志查询页面

【JMicro】1. JMicro简介
大家有任何问题欢迎评论。

如果你对这个项目感兴趣,也欢迎参与开发,共同完善。

后面资金允许后,买个服务器在公网上部署一套,让大家体验!