-
云原生微服务框架——Helidon
2019-08-04 19:31:53目前市面上已经有不少微服务框架了,再造一个新的似乎没这个必要,不过Oracle还真就这么做了,这个项目便是Helidon。光看项目名字你可能就知道Oracle为什么要创建这个项目了:Helidon在希腊语中是燕子的意思——一种...在互联网早期的相当长一段时间内,WEB应用都是”单体应用(monolithic)“。也就是说所有的API和前端展示层代码都被封装在一个独立的、自给自足的应用当中。业务逻辑,校验,数据获取及计算,持久化,安全,UI都封装成一个大的包,部署在应用服务器或者web服务器上,比如说Tomcat, Apache或者Microsoft IIS。这个方法过去有效,未来也仍将有效,只不过当你的应用到达一定规模之后,就会面临诸多挑战:
- 部署:对于单体应用来说,checkout源代码,编译,测试,打包和部署这些都需要花费相当长的时间。
- 依赖关系,框架及开发语言:整个应用都和具体的选型和版本强绑定,一旦这些基础框架发布了新的版本,升级会非常困难。
- 单点故障:单体应用非常脆弱,如果web服务器挂了,整个应用也就挂了。
- 扩展性:哪怕只是应用程序其中的某一部分引发的负载升高,也必须对整个应用来进行扩容。
当然还会碰到其它问题,但这些就已经够让开发人员、项目经理、运维人员头疼的了。长久以来,大家不得不去处理这些事情。
- 部署:每个服务都可以单独地测试、编译及部署。
- 依赖关系、框架及开发语言:每个服务都可以使用自己所需要的开发语言、框架、依赖及不同的版本。
- 单点故障:每个服务都部署在容器里并使用编排工具来管理,单点宕机会被隔离掉,不会影响到整个应用。
- 扩展性:服务可以独立进行扩展,高负载的服务扩容,低负载的服务缩容。
微服务并不是万能的,但在许多场景下还是非常有用的。我们已经介绍了“为什么”需要微服务,现在来介绍下”如何“实现微服务。
目前市面上已经有不少微服务框架了,再造一个新的似乎没这个必要,不过Oracle还真就这么做了,这个项目便是Helidon。光看项目名字你可能就知道Oracle为什么要创建这个项目了:Helidon在希腊语中是燕子的意思——一种小巧、灵活的鸟类,它们天然就适合在云端翱翔。因此,这个项目的发起人应该是想开发出一款无需应用服务器且能被用于Java SE应用的轻量级框架。
Helidon有两种版本:SE和MP。Helidon SE算是一个微框架(microframework),比较简单、轻量,采用了函数式编程、响应式编程的思想,运行在自带的Netty web服务器上。它比较类似于Javalin、Micronaut或者Spark Java这样的框架。而Helidon MP实现了MicroProfile的规范,采用了Java EE/Jakarta EE开发人员所熟知的注解和组件的技术,比如说JAX-RS/Jersey, JSON-P以及CDI。它和Open Liberty, Payara还有Thorntail (正式名称是 WildFly Swarm)的定位差不多。我们先从Helidon SE开始,来了解一下这个框架。
Helidon SE入门
新工具的学习就是在摸着石头过河,不过Helidon不存在这个问题。只需要安装一些必要的依赖软件(JDK 8+,Maven 3.5+)就可以开始使用了。使用Docker或者Kubernetes的话,能让容器的创建和部署更加容易。那还需要安装Docker 18.02或更新的版本,以及Kubernetes 1.7.4+。(可以使用Minikube或Docket Desktop在桌面操作系统上运行你的Kubernetes集群)。
确认下软件的版本:
$ java --version $ mvn --version $ docker --version $ kubectl version --short
一旦安装完成,便能够通过Helidon提供的Maven项目模板(原型,Archetype)来快速生成一个工程。可能你对Maven Archetype还不太了解,它其实就是一些项目模板,可以用来搭建某个框架的启动工程以便快速使用。Oracle提供了两套项目模板:Helidon SE和Helidon MP各一个。
mvn archetype:generate -DinteractiveMode=false \ -DarchetypeGroupId=io.helidon.archetypes \ -DarchetypeArtifactId=helidon-quickstart-se \ -DarchetypeVersion=0.10.2 \ -DgroupId=[io.helidon.examples] \ -DartifactId=[quickstart-se] \ -Dpackage=[io.helidon.examples.quickstart.se]
项目模板在Maven的中央仓库中,在这里你可以找到最新发布的版本。前面方括号内的值是和具体项目相关的,可以根据你的需要来进行编辑。本文中的示例将使用下面的命令来创建完成:
$ mvn archetype:generate -DinteractiveMode=false \ -DarchetypeGroupId=io.helidon.archetypes \ -DarchetypeArtifactId=helidon-quickstart-se \ -DarchetypeVersion=0.10.2 \ -DgroupId=codes.recursive \ -DartifactId=helidon-se-demo \ -Dpackage=codes.recursive.helidon.se.demo
完成之后,一个完整的示例工程就会在新生成的目录当中了,目录名便是artifactId参数里所指定的。这是一个完整的可运行的工程,可以编译打包一下:
$ mvn package
这个命令会把所有生成的测试用例全执行一遍,并在target/libs目录下生成应用的jar包。这个框架还自带了一个内嵌的web服务器,现在你可以通过下述的命令来运行一下:
$ java -jar target/helidon-se-demo.jar
可以看到应用程序启动起来了,工作在8080端口上:
[DEBUG] (main) Using Console logging 2018.10.18 14:34:10 INFO io.netty.util.internal.PlatformDependent Thread[main,5,main]: Your platform does not provide complete low-level API for accessing direct buffers reliably. Unless explicitly requested, heap buffer will always be preferred to avoid potential system instability. 2018.10.18 14:34:10 INFO io.helidon.webserver.netty.NettyWebServer Thread[nioEventLoopGroup-2-1,10,main]: Channel '@default' started: [id: 0x3002c88a, L:/0:0:0:0:0:0:0:0:8080] WEB server is up! http://localhost:8080
但是访问根路径会报错,因为这个模板并没有在根路径下配置路由。你可以访问http://localhost:8080/greet,它会返回一个JSON格式的”Hello Wrold“的信息。
到目前为止,除了执行了几个maven命令并启动应用之外,我们没写过一行代码,却已经有了一个搭建好的完整的可运行的应用程序。当然了,未来还是要和代码打交道的,不过在这之前我们先来看下Helidon为Docker提供了什么样的支持。
我们先通过ctrl+c把程序停掉。在target目录中,我们可以看到运行mvn package命令时额外生成了一些文件。Helidon生成了一个可以用来构建Docker容器的Dockerfile,以及一个用于部署到Kubernetes的application.yaml。这两文件虽然简单,但有了它们可以很快把应用部署起来。
下面是这个demo工程的Dockerfile(为了简洁起见,授权信息就去掉了):
FROM openjdk:8-jre-alpine RUN mkdir /app COPY libs /app/libs COPY helidon-se-demo.jar /app CMD ["java", "-jar", "/app/helidon-se-demo.jar"]
也许你是第一次接触Dockerfile,在首行它声明了一个基础的镜像。这里用的是8-jre-alpine的openjdk镜像,这是基于Alpine Linux的包含了Java 8 JRE的一个非常轻量级的镜像。两行之后,Dockerfile创建了一个app目录来存储应用程序。接下来这行将libs目录中的文件拷贝到app/libs下,然后将jar也包复制到app下。最后一行告诉Docker执行java jar命令来启动应用。
我们在工程的根目录下运行下面的命令来测试一下这个Dockerfile:
(注:如果你用的是kubemini,在运行后面的docker build命令前,一定要先执行下:
eval $(minikube docker-env)
否则后面kubernetes会找不到镜像。)
$ docker build -t helidon-se-demo target
它会告诉Docker使用target目录下的Dockerfile去创建一个tag为helidon-se-demo的镜像。执行完docker builld的输出结果大概是这样的:
Sending build context to Docker daemon 5.231MB Step 1/5 : FROM openjdk:8-jre-alpine ---> 0fe3f0d1ee48 Step 2/5 : RUN mkdir /app ---> Using cache ---> ab57483b1f76 Step 3/5 : COPY libs /app/libs ---> 6ac2b96f4b9b Step 4/5 : COPY helidon-se-demo.jar /app ---> 7d2135433bcc Step 5/5 : CMD ["java", "-jar", "/app/helidon-se-demo.jar"] ---> Running in 5ab71094a72f Removing intermediate container 5ab71094a72f ---> 7e81289d5267 Successfully built 7e81289d5267 Successfully tagged helidon-se-demo:latest
运行下这个命令确认下结果是否ok:
docker images helidon-se-demo
你可以在目录下找到一个叫helidon-se-demo的容器文件。我这里生成的文件大小是88.2MB。通过下面的命令来启动这个容器:
$ docker run -d -p 8080:8080 helidon-se-demo
docker run命令加上-d开关后会在后台运行容器实例,-p开关用来指定端口。最后是要运行的镜像名,这里是helidon-se-demo。
如果想看下你的系统中有哪些容器在运行,可以使用这个命令:
$ docker ps -a
你也可以使用像Kitematic或Portainer这样的GUI工具。我个人比较喜欢Portainer,现在用它来看下运行状态,结果如图一所示。
当然你也可以访问http:localhost:8080/greet来确认下应用程序是否还在本地运行着(只不过这次它是运行在Docker里了)。
在Kubernetes中运行
了解完Helidon对Docker的支持度后,我们再来看看它对Kubernetes支持得怎么样。先kill掉Docker容器(命令行或GUI工具都可以)。然后看一下生成的target/app.yaml文件。它的内容如下:
kind: Service apiVersion: v1 metadata: name: helidon-se-demo labels: app: helidon-se-demo spec: type: NodePort selector: app: helidon-se-demo ports: - port: 8080 targetPort: 8080 name: http --- kind: Deployment apiVersion: extensions/v1beta1 metadata: name: helidon-se-demo spec: replicas: 1 template: metadata: labels: app: helidon-se-demo version: v1 spec: containers: - name: helidon-se-demo image: helidon-se-demo imagePullPolicy: IfNotPresent ports: - containerPort: 8080 ---
这里我不再详细介绍配置细节,你可以用它来快速地将应用部署到Kubernetes中,Kubernetes则提供了容器管理和编排的能力。通过下述命令将它部署到Kubernetes集群里(同样的,也是工程根目录下执行,否则的话需要更新下app.yaml的路径):
$ kubectl create -f target/app.yaml
如果一切正常,应该能看到这样的结果:
service/helidon-se-demo created deployment.extensions/helidon-se-demo created
可以通过kubectl get deployments来确认下部署情况,kubectl get services可以用来检查服务状态:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) helidon-se-demo NodePort 10.105.215.173 <none> 8080:32700/TCP
可以看到现在服务运行在32700端口上,你可以在浏览器中访问该地址确认一下。
目前为止我们已经搭建好一个应用、生成Docker容器,并且部署到了Kubernetes中——仍然没有写过一行代码。
那现在我们就换一换,来看一下代码。打开src/main/java/Main.java,看一看startServer()方法中Helidon SE是如何初始化内嵌的Netty服务器的:
protected static WebServer startServer() throws IOException { // load logging configuration LogManager.getLogManager().readConfiguration( Main.class.getResourceAsStream("/logging.properties")); // By default this will pick up application.yaml from // the classpath Config config = Config.create(); // Get web server config from the "server" section of // application.yaml ServerConfiguration serverConfig = ServerConfiguration.fromConfig(config.get("server")); WebServer server = WebServer.create(serverConfig, createRouting()); // Start the server and print some info. server.start().thenAccept(ws -> { System.out.println( "WEB server is up! http://localhost:" + ws.port()); }); // Server threads are not demon. NO need to block. Just react. server.whenShutdown().thenRun(() -> System.out.println("WEB server is DOWN. Goodbye!")); return server; }
代码中生成的注释已经解释的很清楚了,总结一下:
- 日志初始化:从生成的application.yaml中获取配置信息(额外的配置变量可以放到这里)
- 通过配置文件中的host/port信息来创建一个ServerConfiguration实例。
- 创建并启动WebServer实例,将createRouting()返回的路由信息传给它。
createRouting()方法是这样注册服务的:
private static Routing createRouting() { return Routing.builder() .register(JsonSupport.get()) .register("/greet", new GreetService()) .build(); }
这里我们注册了"/greet"服务,指向了GreetService。会看到有几个类变量通过Config从前面提到的application.yaml文件中获取配置值。
private static final Config CONFIG = Config.create().get("app"); private static String greeting = CONFIG.get("greeting").asString("Ciao");
GreetService类实现了Service接口并重写了update()方法,里面定义了子路径/greet的实现。
@Override public final void update(final Routing.Rules rules) { rules .get("/", this::getDefaultMessage) .get("/{name}", this::getMessage) .put("/greeting/{greeting}", this::updateGreeting); }
update()方法接收Routing.Rules的实例对象,Routing.Rules的方法分别对应着不同的HTTP请求——get(),post(),put(),head(),options()和trace()——还有一些比较有用的方法比如any(),它可以用来兜底,实现一些日志或安全类的功能。
这里我注册了三个endpoint:/greet/, /greet/{name}和/greet/greeting。每个endpoint都有一个指向服务方法的引用。注册成endpoint的方法接收两个参数:request和response。这样设计的话,你可以从request中获取参数,比如请求头及参数,也可以往response中设置响应头及响应体。getDefaultMessage()方法的内容如下:
private void getDefaultMessage(final ServerRequest request, final ServerResponse response) { String msg = String.format("%s %s!", greeting, "World"); JsonObject returnObject = Json.createObjectBuilder() .add("message", msg) .build(); response.send(returnObject); }
这是个非常简单的例子,但是也能看出服务方法的基本实现结构。getMessage()方法是一个动态路径参数({name}参数是在URL路径中注册进来的)的例子,你可以从URL中获取参数。
private void getMessage(final ServerRequest request, final ServerResponse response) { String name = request.path().param("name"); String msg = String.format("%s %s!", greeting, name); JsonObject returnObject = Json.createObjectBuilder() .add("message", msg) .build(); response.send(returnObject); }
http://localhost:8080/greet/todd的结果如图二所示。
下面要讲的updateGreeting()方法和getMessage()有很大的不同,需要注意的是这里只能调用Put方法而不是get,因为在update()里就是这样注册的。
private void updateGreeting(final ServerRequest request, final ServerResponse response) { greeting = request.path().param("greeting"); JsonObject returnObject = Json.createObjectBuilder() .add("greeting", greeting) .build(); response.send(returnObject); }
Helidon SE还包含很多东西,包括异常处理、静态内容、metrics以及健康度。强烈推荐阅读下项目文档来了解更多特性。
Helidon MP入门
Helidon MP是MicroProfile规范的实现版本。如果你使用过Java EE的话应该不会觉得陌生。前面也提到,你可能会看到像JAX-RS/Jersey, JSON-P以及CDI这些常用的东西。
和Helidon SE一样,我们先通过Helidon MP的项目模板来快速创建一个工程:
$ mvn archetype:generate -DinteractiveMode=false \ -DarchetypeGroupId=io.helidon.archetypes \ -DarchetypeArtifactId=helidon-quickstart-mp \ -DarchetypeVersion=0.10.2 \ -DgroupId=codes.recursive \ -DartifactId=helidon-mp-demo \ -Dpackage=codes.recursive.helidon.mp.demo
看一下Main.java类,你会发现它比Helidon SE还要简单。
protected static Server startServer() throws IOException { // load logging configuration LogManager.getLogManager().readConfiguration( Main.class.getResourceAsStream("/logging.properties")); // Server will automatically pick up configuration from // microprofile-config.properties Server server = Server.create(); server.start(); return server; }
应用的定义在GreetApplication类中,它的getClasses()方法中注册了路由资源。
@ApplicationScoped @ApplicationPath("/") public class GreetApplication extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> set = new HashSet<>(); set.add(GreetResource.class); return Collections.unmodifiableSet(set); } }
Helidon MP中的GreetResource和Helidon SE中的GreetService的角色差不多。不过它不用单独去注册路由信息,你可以使用注解来表示endpoint、HTTP方法和content-type头。
@Path("/greet") @RequestScoped public class GreetResource { private static String greeting = null; @Inject public GreetResource(@ConfigProperty(name = "app.greeting") final String greetingConfig) { if (this.greeting == null) { this.greeting = greetingConfig; } } @Path("/") @GET @Produces(MediaType.APPLICATION_JSON) public JsonObject getDefaultMessage() { String msg = String.format("%s %s!", greeting, "World"); JsonObject returnObject = Json.createObjectBuilder() .add("message", msg) .build(); return returnObject; } @Path("/{name}") @GET @Produces(MediaType.APPLICATION_JSON) public JsonObject getMessage(@PathParam("name") final String name){ String msg = String.format("%s %s!", greeting, name); JsonObject returnObject = Json.createObjectBuilder() .add("message", msg) .build(); return returnObject; } @Path("/greeting/{greeting}") @PUT @Produces(MediaType.APPLICATION_JSON) public JsonObject updateGreeting(@PathParam("greeting") final String newGreeting) { this.greeting = newGreeting; JsonObject returnObject = Json.createObjectBuilder() .add("greeting", this.greeting) .build(); return returnObject; } }
结论
Helidon MP和Helidon SE的区别还不止这些,但它们的目标都是一致的,即降低微服务的使用门槛。Helidon是一个功能非常强大的框架,能够帮助你快速开发微服务应用。如果你不希望使用容器技术,你也可以像部署传统jar一样去部署它。如果你的团队使用容器技术,它内建的支持能够帮忙你快速地部署到任何云上或自有的Kubernetes集群中。由于Helidon是Oracle公司开发的,因此团队后续会计划将它集成到Oracle Cloud上。如果你已经在使用Oracle Cloud部署应用,或者最近有计划要迁移到上面,那么Helidon将是你的不二选择。
-
-
云原生quarkus框架项目实践 微服务框架
2020-09-22 16:18:52quarkus是Redhat开源的云原生微服务框架, 相比较成熟的SpringCloud, 为什么要用quarkus? 主要有以下几点原因: Spring系列框架臃肿、复杂, 更像是一个全家桶. 而quarkus 简单、高效, 工具先进 启动速度, quarkus...一、quarkus 是什么?为什么要用quarkus
quarkus是Redhat开源的云原生微服务框架, 相比较成熟的SpringCloud, 为什么要用quarkus?
主要有以下几点原因:
- Spring系列框架臃肿、复杂, 更像是一个全家桶. 而quarkus 简单、高效, 工具先进
- 启动速度, quarkus可以在5秒内启动, 而spring对于一个golang开发者来说, 这个速度直接无法忍受.
- quarkus可以热编译, 无需手动编译和重启服务, 而Spring的热编译..
- 与其他工具集成, Spring集成了大部分的工具, 但你把DI换成guice试试, quarkus可以很方便的集成工具, 虽然框架本身包含的东西不多
- quarkus不依赖tomcat或jetty, 可以编译为原生应用, 性能大幅提高
- quarkus耦合低, 项目结构干净, 适合使用代码生成器.
二、创建一个quarkus项目
您可以使用maven或gradle来快速创建一个quarkus项目, 具体方法见quarkus网站, quarkus 只需要创建一个Resource类, 就可以启动服务. 零配置.
另外:quarkus 对Kotlin支持极为友好, 本文将创建一个使用Kotlin+Gradle的项目. 项目的配置文件: build.gradle.kts内容如下:
plugins{ java kotlin("jvm") version ("1.3.72") kotlin("plugin.allopen") version ("1.3.72") id("io.quarkus") version("1.4.2.Final") } allOpen { annotation("javax.enterprise.context.ApplicationScoped") annotation("javax.enterprise.context.RequestScoped") } repositories { maven("http://maven.aliyun.com/nexus/content/groups/public/") mavenCentral() } dependencies { implementation(kotlin("stdlib")) implementation("io.quarkus:quarkus-kotlin:1.4.2.Final") implementation("io.quarkus:quarkus-resteasy:1.4.2.Final") implementation("io.quarkus:quarkus-resteasy-jsonb:1.4.2.Final") testImplementation("io.quarkus:quarkus-junit5:1.4.2.Final") } tasks.withType<Test> { useJUnitPlatform() } // 代码生成器 tasks.create("generate").doFirst { exec{ workingDir("./tto") commandLine("sh","-c","./tto.sh") } } tasks.withType<JavaCompile>().configureEach { options.encoding="utf-8" options.compilerArgs = listOf("-Xdoclint:none", "-Xlint:none", "-nowarn") }
三、配置并启动项目
您可以创建一个类, 并添加注解:@ApplicationScoped , 作为系统启动类, 代码如下:
@ApplicationScoped class Application { fun onStart(@Observes event: StartupEvent?) { println("app started..") } }
这并不是必须的, 因为上文提到了, 可能需要集成其他工具. 接着我们创建一个服务如下:
import javax.ws.rs.GET import javax.ws.rs.Path import javax.ws.rs.Produces import javax.ws.rs.core.MediaType @Path("/hello") class HelloResource { @GET@Path("/{name}") @Produces(MediaType.APPLICATION_JSON) fun hello(@PathParam("name") name:String): String { return "hello ${name}" } }
运行命令启动服务
gradle quarkusDev
访问服务
curl http://localhost:8080/hello/jarrysix > hello jarrysix
三、使用数据源
通过上面的步骤, 我们已能运行quarkus, 接下来我们通过极为简单的方式来完成数据源的访问.
首先, 我们需要添加配置:
quarkus.datasource.db-kind=h2 quarkus.datasource.username=username-default quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/mem:default quarkus.datasource.jdbc.min-size=3 quarkus.datasource.jdbc.max-size=13
创建实体类
@Entity public class Gift { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="giftSeq") private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } private String name; public String getName() { return name; } }
创建Panachec仓储类
@ApplicationScoped public class PersonRepository implements PanacheRepository<Person> { // put your custom logic here as instance methodspublic void deletePerson(name:String){ delete("name",name); } }
在资源类中调用仓储类
@Path("/person") class HelloResource { @Inject private lateinit var repo:PersonRepository @DELETE@Path("/{name}") fun delete(@PathParam("name") name:String): String { this.repo.deletePerson(name); return "success" } }
当然在实际项目中不建议直接调用仓储, 就这样我们完成人员删除的服务.
三:使用docker打包镜像
quarkus可以通过GraalVM打包成原生镜像, 以在生产环境中得到更低的CPU和内存占用. 如果您不想本地打包, 可以使用docker镜像打包为原生应用.
本文为了简化, 依然使用JVM来运行quarkus, 镜像构建配置文件如下:
# Quarkus docker image demo # Version 1.0 # Author : jarrysix(homepage: http://fze.net) # Date : 2018-04-13 14:40 FROM adoptopenjdk/openjdk14-openj9:alpine-jre MAINTAINER jarrysix WORKDIR /data WORKDIR /app COPY build/*.jar ./ COPY build/lib ./lib RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories && \ apk add tzdata fontconfig ttf-dejavu && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime EXPOSE 8080 ENTRYPOINT ["java","-jar *-runner.jar"]
四:使用代码生成器
因为quarkus的项目结构及对框架和工具依赖较低, 甚至仔细观察, 项目代码里大多引用的就是JAVA自带的工具集. 这样对我们使用代码生成器来生成一些格式重复的代码是相当有利的.
我在生产环境中, 就用生成器来生成quarkus和vue.js的代码. 极大的减少了工作量. 接下来我们一步一步的创建代码模板并生成代码.
注: 文中使用的是go编写的代码生成器:tto , 项目主页: http://github.com/ixre/tto ; 其他工具也可以达到效果
1. 数据实体代码模板: pojo.java
#!target:java/{{.global.Pkg}}/pojo/{{.table.Title}}Entity.java package {{pkg "java" .global.Pkg}}.pojo; import javax.persistence.Basic; import javax.persistence.Id; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; import javax.persistence.GenerationType; import javax.persistence.GeneratedValue; /** {{.table.Comment}} */ @Entity @Table(name = "{{.table.Name}}", schema = "{{.table.Schema}}") public class {{.table.Title}}Entity { {{range $i,$c := .columns}}{{$type := type "java" $c.Type}} {{if $c.IsPk}}\ @Id @GeneratedValue(strategy = GenerationType.IDENTITY){{else}} @Basic{{end}} @Column(name = "{{$c.Name}}"{{if not $c.NotNull}}, nullable = true{{end}} {{if ne $c.Length 0}},length = {{$c.Length}}{{end}}) private {{$type}} {{$c.Name}}; /** {{$c.Comment}} */ public {{$type}} get{{$c.Prop}}() { return this.{{$c.Name}}; } public void set{{$c.Prop}}({{$type}} {{$c.Name}}){ this.{{$c.Name}} = {{$c.Name}}; } {{end}} /** 拷贝数据 */ public {{.table.Title}}Entity copy({{.table.Title}}Entity src){ {{.table.Title}}Entity dst = new {{.table.Title}}Entity(); {{range $i,$c := .columns}} dst.set{{$c.Prop}}(src.get{{$c.Prop}}());{{end}} return dst; } }
2. 仓储代码模板: quarkus_repo.kt
#!target:kotlin/{{.global.Pkg}}/repo/{{.table.Title}}JpaRepository.kt.gen package {{pkg "java" .global.Pkg}}.repo; import {{pkg "kotlin" .global.Pkg}}.pojo.{{.table.Title}}Entity import io.quarkus.hibernate.orm.panache.PanacheRepository import javax.enterprise.context.ApplicationScoped {{$pkType := type "kotlin" .table.PkType}} /** {{.table.Comment}}仓储 */ @ApplicationScoped class {{.table.Title}}JpaRepository : PanacheRepository<{{.table.Title}}Entity> { }
3. 服务代码模板:quarkus_service.kt
#!target:kotlin/{{.global.Pkg}}/service/{{.table.Title}}Service.kt.gen package {{pkg "java" .global.Pkg}}.service import {{pkg "java" .global.Pkg}}.pojo.{{.table.Title}}Entity import {{pkg "java" .global.Pkg}}.repo.{{.table.Title}}JpaRepository import javax.inject.Inject import javax.enterprise.inject.Default import javax.enterprise.context.ApplicationScoped import net.fze.util.catch import net.fze.commons.std.Types import net.fze.commons.std.TypesConv import net.fze.util.value import javax.transaction.Transactional {{$tableTitle := .table.Title}} {{$pkName := .table.Pk}} {{$pkProp := lower_title .table.PkProp}} {{$pkType := type "kotlin" .table.PkType}} /** {{.table.Comment}}服务 */ @ApplicationScoped class {{.table.Title}}Service { @Inject@field:Default private lateinit var repo: {{$tableTitle}}JpaRepository fun parseId(id:Any):Long{return TypesConv.toLong(id)} /** 根据ID查找{{.table.Comment}} */ fun findByIdOrNull(id:{{$pkType}}):{{$tableTitle}}Entity?{ return this.repo.findByIdOptional(this.parseId(id)) } /** 保存{{.table.Comment}} */ @Transactional fun save{{$tableTitle}}(e: {{$tableTitle}}Entity):Error? { return catch { var dst: {{$tableTitle}}Entity if (e.{{$pkProp}} > 0) { dst = this.repo.findById(this.parseId(e.{{$pkProp}}))!! } else { dst = {{$tableTitle}}Entity() {{$c := try_get .columns "create_time"}}\ {{if ne $c nil}}dst.createTime = Types.time.unix().toLong(){{end}} } {{range $i,$c := exclude .columns $pkName "create_time" "update_time"}} dst.{{lower_title $c.Prop}} = e.{{lower_title $c.Prop}}{{end}}\ {{$c := try_get .columns "update_time"}} {{if ne $c nil}}dst.updateTime = Types.time.unix().toLong(){{end}} this.repo.persistAndFlush(dst) null }.error() } /** 批量保存{{.table.Comment}} */ @Transactional fun saveAll{{$tableTitle}}(entities:Iterable<{{$tableTitle}}Entity>){ this.repo.persist(entities) this.repo.flush() } /** 删除{{.table.Comment}} */ @Transactional fun deleteById(id:{{$pkType}}):Error? { return catch { this.repo.deleteById(this.parseId(id)) }.error() } }
4. 资源类代码模板:restful_resource.kt
#!target:kotlin/{{.global.Pkg}}/resources/{{.table.Title}}Resource.kt.gen package {{pkg "java" .global.Pkg}}.resources import {{pkg "java" .global.Pkg}}.pojo.{{.table.Title}}Entity import {{pkg "java" .global.Pkg}}.service.{{.table.Title}}Service import {{pkg "java" .global.Pkg}}.component.TinyQueryComponent import net.fze.commons.std.Result import net.fze.component.report.DataResult import javax.inject.Inject import javax.ws.rs.* import javax.ws.rs.core.MediaType import javax.enterprise.context.RequestScoped import javax.annotation.security.PermitAll {{$tableTitle := .table.Title}} {{$pkType := type "kotlin" .table.PkType}} /* {{.table.Comment}}资源 */ @Path("/{{.table.Name}}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @RequestScoped class {{.table.Title}}Resource { @Inject private lateinit var service:{{.table.Title}}Service @Inject private lateinit var queryComponent: TinyQueryComponent /** 获取{{.table.Comment}} */ @GET@Path("/{id}") @PermitAll fun get(@PathParam("id") id:{{$pkType}}): {{.table.Title}}Entity? { return service.findByIdOrNull(id) } /** 创建{{.table.Comment}} */ @POST @PermitAll fun create(entity: {{.table.Title}}Entity):Result { val err = this.service.save{{.table.Title}}(entity) if(err != null)return Result.create(1,err.message) return Result.OK } /** 更新{{.table.Comment}} */ @PUT@Path("/{id}") @PermitAll fun save(@PathParam("id") id:{{$pkType}},entity: {{.table.Title}}Entity):Result { entity.{{lower_title .table.PkProp}} = id val err = this.service.save{{.table.Title}}(entity) if(err != null)return Result.create(1,err.message) return Result.OK } /** 删除{{.table.Comment}} */ @DELETE@Path("/{id}") @PermitAll fun delete(@PathParam("id") id:{{$pkType}}):Result { val err = this.service.deleteById(id) if(err != null)return Result.create(1,err.message) return Result.OK } /** {{.table.Comment}}列表 */ @GET @PermitAll fun list(): List<{{.table.Title}}Entity> { return mutableListOf() } /** {{.table.Comment}}分页数据 */ @GET@Path("/paging") @PermitAll fun paging(@QueryParam("params") params:String, @QueryParam("page") page:String, @QueryParam("rows") rows:String ): DataResult { return this.queryComponent.fetchData("default", "{{.table.Title}}List", params, page, rows) } }
5. VUE接口文件代码模板:api.ts
#!lang:ts#!name:API和定义文件 #!target:ts/feature/{{.table.Prefix}}/{{.table.Name}}/api.ts import request from '@/utils/request' // {{.table.Comment}}对象 export interface I{{.table.Title}} { {{range $i,$c := .columns}}// {{$c.Comment}} {{lower_title $c.Prop}}:{{type "ts" $c.Type}} {{end}} } export const default{{.table.Title}}:()=>I{{.table.Title}}=()=>{ return { {{range $i,$c := .columns}} {{lower_title $c.Prop}}:{{default "ts" $c.Type}},{{end}} }; } export const get{{.table.Title}} = (id: any, params: any = {}) => request({ url: `/{{.table.Name}}/${id}`, method: 'get', params:{...params} }) export const get{{.table.Title}}List = (params: any = {}) => request({ url: '/{{.table.Name}}', method: 'get', params:{...params} }) export const create{{.table.Title}} = (data: any) => request({ url: '/{{.table.Name}}', method: 'post', data }) export const update{{.table.Title}} = (id: any, data: any) => request({ url: `/{{.table.Name}}/${id}`, method: 'put', data }) export const delete{{.table.Title}} = (id: any) => request({ url: `/{{.table.Name}}/${id}`, method: 'delete' }); export const batchDelete{{.table.Title}} = (arr: any[]) => request({ url: '/{{.table.Name}}', method: 'delete', data:arr }); export const getPaging{{.table.Title}} = (page:number,rows:number,params: any) => request({ url: '/{{.table.Name}}/paging', method: 'get', params:{page,rows,params} })
运行命令将代码生成到指定位置
gradle generate
-
微服务 杜家豪_从微服务框架到云原生微服务生态,CNCF 峰会上 TARS 基金会分论坛聊了这些...
2020-12-26 16:20:05微服务通常被看做是云原生的代表技术之一,在单体应用中,云原生基本发挥不出优势,故而有人说现代应用程序的趋势就是“微服务+云原生”,以及内含在其中的容器、敏捷与 DevOps 等技术组合。互联网海量和多变的业务...
云原生虽已被公认是新一代云计算发展方向,但若从应用程序的开发、测试、运行以及最后部署到云环境中的过程看,实现云原生业务要打通许多技术。微服务通常被看做是云原生的代表技术之一,在单体应用中,云原生基本发挥不出优势,故而有人说现代应用程序的趋势就是“微服务+云原生”,以及内含在其中的容器、敏捷与 DevOps 等技术组合。互联网海量和多变的业务,正要求多种功能的同时实现,同样是要集合各种技术。8 月 1 日,线上举办的 CloudNative + Open Source Virtual Summit China 2020,TARS 基金会分论坛上发布了 K8STARS 项目,这是一个整合了 K8S 和微服务框架 TARS 功能的项目,对于 TARS 项目本身来说,弥补了其自身的自动扩伸缩等能力,增强了其云原生特性。放到云原生中来看,微服务架构应用系统后期会面临许多挑战,比如持续集成、持续交付、持续部署、监控、认证、授权以及快速配置计算资源等等,而 Kubernetes 提供如资源的调度、弹性伸缩、自动化部署等能力,是许多微服务在实施应用时不可或缺的。正如这几日 TARS 基金会分论坛上 Linux 基金会执行董事 Jim Zemlin 在开场时提到的:TARS 基金会致力于解决在使用微服务方面可能出现的问题,包括减少开发和服务治理的难度。而基金会中最主要的项目 TARS 也不仅仅只是一个 RPC 框架。TARS 基金会成立于今年 3 月 10 日,是 Linux 基金会中微服务开源生态的代表。这次论坛上,TARS 基金会主席单致豪详细介绍了基金会目前的生态,并公布了近期加入 TARS 基金会的 4 个开源项目:测试相关的 TARS Benchmark 与 TARS JMeter,服务网关 TARS Gateway,以及重磅发布的云原生微服务治理方案 K8STARS。HTTP 协议,后端同时支持 TARS-tup&TARS-TARS 协议、TARS-json 协议、HTTP 协议。除了协议转发之外,还支持流量控制与黑白名单等功能;TARS JMeter 是一款针对 TARS 协议进行私有化定制的 JMeter 测试插件,它具有易用性强、支持分布式、支持复杂场景与数据可监控等特点;TARS Benchmark 是专门为 TARS 服务量身订做的无码压测工具,具备易用性、高性能、可伸缩、支持动态随机和数据实时反馈的特性。 下边具体说说 K8STARS。K8STARS:基于 K8S 与 TARS 的云原生微服务治理方案
微服务架构已然成为云原生中最核心的技术之一,TARS 结合 Kubernetes 带来的优势突破或让 TARS 这个微服务框架在云原生生态中更上一层楼,也将让 TARS 基金会覆盖更宽广的领域。先介绍一下 K8STARS,简单来讲,K8STARS 是便于将 TARS 服务运行在 Kubernetes 中的解决方案,它的主要特性包括:• 保持 TARS 原生的开发框架能力
• 支持 TARS 的名字服务自动注册和配置删除
• 支持原有 TARS 服务平滑迁移到 Kubernetes 等容器平台
• 无侵入性设计,与运行环境无偶合关系
腾讯高级工程师、TARSGo 核心开发者利开园在分享中更具体地分析了 K8STARS 解决方案。简单来说,K8STARS 保留了 TARS 的名字服务、高性能 RPC 及服务治理功能,整合了 K8S 的资源调度等能力,使得 TARS 具有更多的云原生特性。微服务的理念下,使用容器作为基础设施,能够实现快速部署、快速迭代。而随着容器化技术兴起,Kubernetes 可以说已经成为基于容器的 DevOps+微服务+容器的黄金标准。整合 K8S 成为许多微服务框架完善功能的一个大方向。利开园总结,业务规模增长会带来四大挑战:• 随着用户流量增长,需要分布式架构,以及更高性能的缓存等;• 为了满足高性能的要求,需要更多的研发人员以及更好的协调,这就需要通用的通信协议、多语言开发框架等;• 随着服务器数量的增长,还有庞大数量的集群需要管理,此时需要机房故障时的自动解决方案;• 最后服务进程数量暴涨,处理好并发的服务也是一个难点。TARS 的主要能力是高效率开发,如代码自动生成、多语言、名字服务等等;高质量运维,如服务可视化、无损变更、配置管理与容灾容错等。“然而,在面对业务规模增长带来的技术挑战时,TARS 也有需要增强的地方。”
“TARS 可以使开发者专注业务逻辑,而不需要关心底层,但是 TARS 过去是没有自动扩伸缩能力的。”利开园介绍:“TARS 需要用到 K8S 的自动调度和自动扩缩容的能力。”
不过 TARS 和 K8S 存在许多重合的能力,想要整合并不简单。同时 K8S 生态中,Istio 是最热门的微服务解决方案,此时是不是直接放弃 TARS 转向 Istio 更好?
中间部分为重合的能力对比了 TARS 和 Istio 的优缺点之后,考虑到 TARS 服务迁移的成本,开发团队觉得有必要使用 TARS,将 TARS 改造成支持在 K8S 中调度的微服务解决方案。
选型中考虑的 TARS 与 Istio 的情况
具体怎么整合呢?TARS 比 K8S 多出的能力包括高性能 RPC 框架、日志监控、调用链以及指标监控等服务治理能力;二者重合的功能有服务部署、版本管理等。整合首先要对重合功能做出取舍,因为 K8S 支持自动调度,基于 Docker 镜像可有更好的版本管理实现;而在名字服务部分,TARS 支持网关流量,发现单点故障时可快速屏蔽问题,配置管理也更适合在生产环境中使用。
通过在 TARSregistry 增加了 3 个接口,用于 TARS 名字的自动注册/心跳上报和节点下线,同时提供一个 TarsCLI 命令行工具,用于分配端口/生成配置/上报心跳以及节点下线,最终整合出了 K8STARS 这一全新的解决方案,让 TARS 服务运行在 Kubernetes 上。具体细节可以查看项目说明与源码:https://github.com/TarsCloud/K8STARS
从 TARS 到 TARS 基金会到云原生微服务开源生态
前边提到,本次分论坛较为全面地对外介绍了成立于今年 3 月的 TARS 基金会的情况,这也是其首次分享其生态相关信息。
作为基金会,发展的是生态而不是单一项目,TARS 基金会介绍其希望吸纳上下游的开源项目,以建立更好的微服务生态。包含但不限于基础设施、存储、开发框架、服务治理、DevOps 和基于任何编程语言的应用。具体来讲,目前基金会中已经有 30+ 项目,7 家成员组织,以及获得了覆盖游戏、视频直播、互联网工具、娱乐、交通、社交网络与金融等领域 100+ 家公司/企业的采用。
此外,分论坛还有来自不同公司不同业务的技术专家分享了 TARS 在其各自领域上的具体应用情况,比如自腾讯的陈德贤、阅文的马延波与虎牙的毛茂德分享了各自业务中采用 TARS 进行微服务架构改造的情况;同时,TarsJava 技术负责人俞慧涛分享了阅文 TARS 的海外最佳实践;Arm 企业级架构师、TARS 技术监督委员会成员 Tina Tsou 分享了如何通过 Arm 架构和 Ampere Computing 支持 TARS 平台上的云原生工作负载加速;来自 Apache APISIX 的 TARS 技术监督委员会成员温铭强调了 API 网关在云原生体系中的重要性;最后腾讯 CSIG 高级测试专家鲁珺聊到了如何实现微服务性能评测。从周边项目、开发者与社区成员的发展情况,从项目演进,到硬件架构扩展,再到应用实践以及性能测试等方面,TARS 基金会分论坛在此次云原生峰会中为开发者呈现了一个正在快速发展中的云原生微服务开源生态。最后,或者这张生态全景图可以很好地勾勒出 TARS 基金会未来的发展全貌,当然,生态还在动态演进,具体会如何发展,是留给 TARS 基金会这一新生组织的一大问题,也是关注云原生微服务开源生态的开发者的一大期待。TARS基金会是Linux基金会下的非营利性、微服务基金会,致力于建设一个强大而灵活的微服务生态系统。无论你在哪个行业,无论你使用什么技术栈,这里能助你快速实现你的创意。
点“在看”让TARS小姐姐变好看
-
Nepxion Polaris 企业级云原生微服务基础架构脚手架的指南,围绕Nepxion Discovery框架打造,...
2021-01-17 08:20:31Polaris【北极星】企业级云原生微服务框架 如果您觉得本框架具有一定的参考价值和借鉴意义,请帮忙在页面右上角 [Star] Polaris【北极星】企业级云原生微服务框架① Polaris【北极星】企业级云原生微服务框架文档② ...Polaris【北极星】企业级云原生微服务框架
如果您觉得本框架具有一定的参考价值和借鉴意义,请帮忙在页面右上角 [Star]
Polaris【北极星】企业级云原生微服务框架
① Polaris【北极星】企业级云原生微服务框架文档
② Polaris【北极星】企业级云原生微服务框架源码。请访问Gitee镜像获得最佳体验
③ Polaris【北极星】企业级云原生微服务框架指南示例源码。请访问Gitee镜像获得最佳体验
Discovery【探索】微服务企业级解决方案
① Discovery【探索】微服务企业级解决方案文档
② Discovery【探索】微服务企业级解决方案源码。请访问Gitee镜像获得最佳体验
③ Discovery【探索】微服务企业级解决方案指南示例源码。请访问Gitee镜像获得最佳体验
④ Discovery【探索】微服务框架指南示例说明
对于入门级玩家,参考指南示例极简版,分支为simple。涉及到指南篇里的灰度路由和发布的基本功能,
参考新手快速入门
对于熟练级玩家,参考指南示例精进版,分支为master。除上述《极简版》功能外,涉及到指南篇里的绝大多数高级功能
对于骨灰级玩家,参考指南示例高级版,分支为premium。除上述《精进版》功能外,涉及到指南篇里的ActiveMQ、MongoDB、RabbitMQ、Redis、RocketMQ、MySQL等高级调用链和灰度调用链的整合
Discovery【探索】和Polaris【北极星】联合架构图
Discovery【探索】和Polaris【北极星】联合拓扑图
Polaris【北极星】分层架构图
Discovery【探索】实施方案图
Discovery【探索】域网关实施图
Discovery【探索】非域网关实施图
Discovery【探索】全局订阅实施图
请联系我
微信、公众号和文档
Star走势图
-
Dubbo 3.0 - 开启下一代云原生微服务
2020-08-26 13:37:06简介:本文整理自作者于 2020 年云原生微服务大会上的分享《Dubbo3.0 - 开启下一代云原生微服务》,主要介绍了关于思考 rpc 框架层面,功能演进的方向是什么?以及怎么更好地支持云上的多语言开发的新思考。作者 | ... -
Dubbo学习总结(11)——Dubbo3.0 - 开启下一代云原生微服务
2020-09-03 10:18:18导读:本文整理自作者于 2020 年云原生微服务大会上的分享《Dubbo3.0 - 开启下一代云原生微服务》,主要介绍了关于思考 rpc 框架层面,功能演进的方向是什么?以及怎么更好地支持云上的多语言开发的新思考。 看到这... -
dubbo k8s 服务发现_Dubbo 3.0 - 开启下一代云原生微服务
2020-12-28 09:26:48作者 | 郭浩(项升) 阿里巴巴经济体 RPC 框架负责人导读:本文整理自作者于 2020 年云原生微服务大会上的分享《Dubbo3.0 - 开启下一代云原生微服务》,主要介绍了关于思考 rpc 框架层面,功能演进的方向是什么?... -
Go语言云原生与微服务(三)Kratos微服务框架学习
2020-12-13 21:23:19常见的微服务框架 一、Go Kit框架 这是一个工具包的集合,可以帮助攻城狮构建强大、可靠和可维护的微服务。提供了用于实现系统监控和弹性模式组件的库,例如日志、跟踪、限流、熔断等。 基于这个框架的应用程序架构... -
android 运行 go_Go 每日一库之云原生 gozero 微服务框架
2020-12-27 17:54:07go-zero 介绍 go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件一键... -
云原生 go-zero 微服务框架
2020-10-13 11:46:56go-zero是一个集成了各种工程实践的web和rpc框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。 go-zero包含极简的API定义和生成工具goctl,可以根据定义的api文件一键生成Go, iOS, Android, ... -
2018中国云原生用户大会:网易云深度解析微服务框架
2019-09-18 13:19:46近日,由才云科技、K8sMeetup 中国社区、Kubeflow 中国社区联合主办的 2018 中国云原生用户大会在杭州白马湖建国饭店举办,大会集结了来自各个行业领域的云原生技术专家、云原生技术落地企业代表、以及云原生技术... -
云原生 go-zero 微服务框架介绍
2020-08-24 09:55:060. go-zero 介绍go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。go-zero 包含极简的 ... -
面向云原生的下一代微服务框架设计与实现
2021-03-06 21:57:47K8S在物理机直接部署和虚拟机部署的区别是什么 首先从容器运行环境上没有区别;其次从隔离上考虑,虚拟机+容器,相比物理机+容器,应用的隔离性可能更好一些; 其次网络规划有影响,k8s...云原生服务架构 ... -
1. 微服务框架 云原生开发基础 2020.3.25
2020-03-26 00:54:392.微服务定义 3.微服务(Microservices)vs整体服务(Monolithic Application) 4. 详细解读微服务 5.为什么用微服务 6.微服务的缺点 7.微服务架构原则 微服务偏向轻量型嵌入式 例如Spring Boot Eclipse Vert.x ... -
android 运行 go_Go 每日一库之云原生 go-zero 微服务框架
2020-12-27 17:54:070. go-zero 介绍go-zero 是一个集成了各种工程实践的 web 和 rpc 框架。通过弹性设计保障了大并发服务端的稳定性,经受了充分的实战检验。go-zero 包含极简的 API 定义和生成工具 goctl,可以根据定义的 api 文件... -
Go语言云原生与微服务(一)云原生架构
2020-12-02 15:39:03本人在学习云原生与微服务架构中的总结资料,参考书籍《Go语言高并发与微服务实战》 仅以此文记录学习过程。 云原生架构之前(即传统飞云原生应用),底层平台负责向上提供运行资源,而应用需要满足业务需求和非... -
【招聘】欢迎热衷于微服务框架、中间件、云原生方向的同志!
2021-01-26 17:04:05希望能招募到热衷于高并发、高可用、高性能,面向云原生技术架构的同志! 有意向的同志,欢迎发简历到aitangyong@oppo.com! 岗位名称: 数据库中间件工程师 工作内容: 1、负责数据库网格产品的架构设计和系统... -
Micronaut:面向未来的微服务和云原生应用框架
2019-10-28 16:45:07转自:https://yq.aliyun.com/articles/649920 -
Micronaut:面向未来的微服务和云原生应用框架
2018-10-10 10:25:41依赖注入、控制反转和面向切面编程,是Micronaut有别于Spring Boot这类传统框架的核心部分。关于这几个概念,我推荐不了解的同学浏览下面的文章,轻松易懂。 轻松理解AOP(面向切面编程) https:...