Kratos微服务与它的小伙伴系列 - ORM框架 - Ent

本文涉及的产品
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: ent 是Facebook开源的一个简单但是功能强大的ORM框架,它可以轻松构建和维护具有大型数据模型的应用程序。它基于代码生成,并且可以很容易地进行数据库查询以及图遍历。

什么是ORM?

面向对象编程和关系型数据库,都是目前最流行的技术,但是它们的模型是不一样的。

面向对象编程把所有实体看成对象(object),关系型数据库则是采用实体之间的关系(relation)连接数据。很早就有人提出,关系也可以用对象表达,这样的话,就能使用面向对象编程,来操作关系型数据库。

简单说,ORM 就是通过实例对象的语法,完成关系型数据库的操作的技术,是"对象-关系映射"(Object/Relational Mapping) 的缩写。

ORM 把数据库映射成对象。

  • 数据库的表(table) --> 类(class)
  • 记录(record,行数据)--> 对象(object)
  • 字段(field)--> 对象的属性(attribute)

举例来说,下面是一行 SQL 语句。

SELECT id, first_name, last_name, phone, birth_date, sex
FROM persons 
WHERE id = 10

程序直接运行 SQL,操作数据库的写法如下。

res = db.execSql(sql);
name = res[0]["FIRST_NAME"];

改成 ORM 的写法如下。

p = Person.get(10);
name = p.first_name;

一比较就可以发现,ORM 使用对象,封装了数据库操作,因此可以不碰 SQL 语言。开发者只使用面向对象编程,与数据对象直接交互,不用关心底层数据库。

ORM 有下面这些优点:

  • 数据模型都在一个地方定义,更容易更新和维护,也利于重用代码。
  • ORM 有现成的工具,很多功能都可以自动完成,比如数据消毒、预处理、事务等等。
  • 它迫使你使用 MVC 架构,ORM 就是天然的 Model,最终使代码更清晰。
  • 基于 ORM 的业务代码比较简单,代码量少,语义性好,容易理解。
  • 你不必编写性能不佳的 SQL。

ORM 也有很突出的缺点:

  • ORM 库不是轻量级工具,需要花很多精力学习和设置。
  • 对于复杂的查询,ORM 要么是无法表达,要么是性能不如原生的 SQL。
  • ORM 抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的 SQL。

什么是Ent?

ent 是Facebook开源的一个简单但是功能强大的ORM框架,它可以轻松构建和维护具有大型数据模型的应用程序。它基于代码生成,并且可以很容易地进行数据库查询以及图遍历。

它具有以下的特点:

  • 简单地使用数据库结构作为图结构。
  • 使用Go代码定义结构。
  • 基于代码生成的静态类型。
  • 容易地进行数据库查询和图遍历。
  • 容易地使用Go模板扩展和自定义。
  • 多存储驱动程序 - 支持MySQL、PostgreSQL、SQLite 和 Gremlin。

如何去学习Ent?

想要上手ent,需要学习和了解三个方面:

  1. entc
  2. Schema
  3. CURD API

Ent因为是基于代码生成的,所以,首当其冲的,自然是要去了解其CLI工具,没有它,如何去生成代码?

其次就是生成代码的模板:Schema。它主要是定义了表结构信息,至关重要的核心信息。生成数据库的结构和操作代码需要它,生成gRPC和GraphQL的接口也还是需要它。没它不行。

最后,就是学习使用一些数据库的基本操作,比如:连接数据库,CURD API。

从此往后,你就能够使用ent愉快的开始工作了。

CLI工具

使用以下命令安装entc工具:

go install entgo.io/ent/cmd/ent@latest

Schema

Schema相当于数据库的表。

《道德经》说:

道生一,一生二,二生三,三生万物。

Schema,就是一切的起始点。

只有定义了Schema,CLI才能够生成数据库表的结构和操作的相关代码,有了相关代码,才能够操作数据库表的数据。

后面想要生成gRPC和GraphQL的接口定义,也还是需要Schema。

创建一个Schema

创建Schema有两个方法可以做到:

使用 entc init 创建

ent init User

将会在 {当前目录}/ent/schema/ 下生成一个user.go文件,如果没有文件夹,则会创建一个:

package schema

import "entgo.io/ent"

// User holds the schema definition for the User entity.
type User struct {
    ent.Schema
}

// Fields of the User.
func (User) Fields() []ent.Field {
    return nil
}

// Edges of the User.
func (User) Edges() []ent.Edge {
    return nil
}

SQL转换Schema在线工具

网上有人好心的制作了一个在线工具,可以将SQL转换成schema代码,实际应用中,这是非常方便的!

SQL转Schema工具: https://printlove.cn/tools/sql2ent

比如,我们有一个创建表的SQL语句:

CREATE TABLE `user`  (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`email` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = DYNAMIC;

转换之后,生成如下的Schema代码:

package schema

import (
    "entgo.io/ent"
    "entgo.io/ent/dialect"
    "entgo.io/ent/schema/field"
)

// User holds the schema definition for the User entity.
type User struct {
    ent.Schema
}

// Fields of the User.
func (User) Fields() []ent.Field {

    return []ent.Field{

        field.Int32("id").SchemaType(map[string]string{
            dialect.MySQL: "int(10)UNSIGNED", // Override MySQL.
        }).NonNegative().Unique(),

        field.String("email").SchemaType(map[string]string{
            dialect.MySQL: "varchar(50)", // Override MySQL.
        }),

        field.String("type").SchemaType(map[string]string{
            dialect.MySQL: "varchar(20)", // Override MySQL.
        }),

        field.Time("created_at").SchemaType(map[string]string{
            dialect.MySQL: "timestamp", // Override MySQL.
        }).Optional(),

        field.Time("updated_at").SchemaType(map[string]string{
            dialect.MySQL: "timestamp", // Override MySQL.
        }).Optional(),
    }

}

// Edges of the User.
func (User) Edges() []ent.Edge {
    return nil
}

Mixin复用字段

在实际应用中,我们经常需要会有一些通用的字段,比如:idcreated_atupdated_at等等。

那么,我们就一直的复制粘贴?这显然很是不优雅。

entgo能够让我们复用这些字段吗?

答案显然是,没问题。

Mixin,就是办这个事儿的。

好,我们现在需要复用时间相关的字段:created_atupdated_at,那么我们可以:

package mixin

import (
    "time"

    "entgo.io/ent"
    "entgo.io/ent/schema/field"
    "entgo.io/ent/schema/mixin"
)

type TimeMixin struct {
    mixin.Schema
}

func (TimeMixin) Fields() []ent.Field {
    return []ent.Field{
        field.Time("created_at").
            Immutable().
            Default(time.Now),
        field.Time("updated_at").
            Default(time.Now).
            UpdateDefault(time.Now),
        field.Bool("deleted").Default(false),
    }
}

然后,我们就可以在Schema当中应用了,比如User,我们为它添加一个Mixin方法:

func (User) Mixin() []ent.Mixin {
    return []ent.Mixin{
        mixin.TimeMixin{},
    }
}

生成代码再看,就有这3个字段了。

生成代码

有了以上的Schema,我们就可以生成代码了。生成代码只能够官方提供的CLI工具ent来生成。

而使用CLI有两种途径可以走:直接使用命令行执行命令,还有一种就是利用了go的go:generate特性。

命令行直接执行命令生成

我们可以命令行进入ent文件夹,然后执行以下命令:

ent generate ./schema

通过 generate.go 生成

直接运行命令看起来是没有问题,但是在我们实际应用当中,直接使用命令行的方式进行代码生成是很不方便的。

为什么呢?ent命令是有参数的,而在正常情况下,都是需要携带一些参数的:比如:--feature sql/modifier,具体文档在:特性开关

这时候,我们必须在某一个地方记录这些命令,而后续会有同事需要接手这个项目呢?他又从何而知?在这个时候就徒增了不少麻烦。

好在go有一个很赞的特性go:generate,可以完美的解决这样一个问题。命令可以以代码的形式被记录下来,方便的重复使用。

通常我们都会把ent相关的代码放置在ent文件夹下面,因此我们在ent文件夹下面创建一个generate.go文件:

package ent

//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate --feature privacy --feature sql/modifier --feature entql --feature sql/upsert ./schema

接着,我们可以在项目的根目录或者ent文件夹下,执行以下命令:

go generate ./...

以上的命令会遍历执行当前以及所有子目录下面的go:generate

如果您使用的是Goland或者VSC,则可以在IDE中直接运行go:generate命令。

ent的一些数据库基本操作

因为数据库是复杂的,SQL是复杂的,复杂到能够出好几本书,所以是绝不可能在简单的篇幅里面讲完整,只能够将常用的一些操作(连接数据库、CURD)拿来举例讲讲。

连接数据库

SQLite3

import (
    _ "github.com/mattn/go-sqlite3"
)

client, err := ent.Open("sqlite3", "file:ent?mode=memory&cache=shared&_fk=1")
if err != nil {
    log.Fatalf("failed opening connection to sqlite: %v", err)
}
defer client.Close()

MySQL/MariaDB

  • TiDB 高度兼容MySQL 5.7 协议
  • ClickHouse 支持MySQL wire通讯协议
import (
    _ "github.com/go-sql-driver/mysql"
)

client, err := ent.Open("mysql", "<user>:<pass>@tcp(<host>:<port>)/<database>?parseTime=True")
if err != nil {
    log.Fatalf("failed opening connection to mysql: %v", err)
}
defer client.Close()

PostgreSQL

  • CockroachDB 兼容PostgreSQL协议
import (
    _ "github.com/lib/pq"
)

client, err := ent.Open("postgresql", "host=<host> port=<port> user=<user> dbname=<database> password=<pass>")
if err != nil {
    log.Fatalf("failed opening connection to postgres: %v", err)
}
defer client.Close()

Gremlin

import (
    "<project>/ent"
)

client, err := ent.Open("gremlin", "http://localhost:8182")
if err != nil {
    log.Fatalf("failed opening connection to gremlin: %v", err)
}
defer client.Close()

自定义驱动sql.DB

有以下两种途径可以达成:

package main

import (
    "time"

    "<your_project>/ent"
    "entgo.io/ent/dialect/sql"
)

func Open() (*ent.Client, error) {
    drv, err := sql.Open("mysql", "<mysql-dsn>")
    if err != nil {
        return nil, err
    }
    // Get the underlying sql.DB object of the driver.
    db := drv.DB()
    db.SetMaxIdleConns(10)
    db.SetMaxOpenConns(100)
    db.SetConnMaxLifetime(time.Hour)
    return ent.NewClient(ent.Driver(drv)), nil
}

第二种是:

package main

import (
    "database/sql"
    "time"

    "<your_project>/ent"
    entsql "entgo.io/ent/dialect/sql"
)

func Open() (*ent.Client, error) {
    db, err := sql.Open("mysql", "<mysql-dsn>")
    if err != nil {
        return nil, err
    }
    db.SetMaxIdleConns(10)
    db.SetMaxOpenConns(100)
    db.SetConnMaxLifetime(time.Hour)
    // Create an ent.Driver from `db`.
    drv := entsql.OpenDB("mysql", db)
    return ent.NewClient(ent.Driver(drv)), nil
}

自动迁移 Automatic Migration

if err := client.Schema.Create(context.Background(), migrate.WithForeignKeys(false)); err != nil {
    l.Fatalf("failed creating schema resources: %v", err)
}

增 Create

pedro := client.Pet.
    Create().
    SetName("pedro").
    SaveX(ctx)

删 Delete

err := client.User.
    DeleteOneID(id).
    Exec(ctx)

改 Update

pedro, err := client.Pet.
    UpdateOneID(id).
    SetName("pedro").
    SetOwnerID(owner).
    Save(ctx)

查 Read

names, err := client.Pet.
    Query().
    Select(pet.FieldName).
    Strings(ctx)

事务 Transaction

事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。

封装一个方法WithTx,利用匿名函数来调用被事务管理的Insert、Update、Delete语句:

package data

func WithTx(ctx context.Context, client *ent.Client, fn func(tx *ent.Tx) error) error {
    tx, err := client.Tx(ctx)
    if err != nil {
        return err
    }
    defer func() {
        if v := recover(); v != nil {
            tx.Rollback()
            panic(v)
        }
    }()
    if err := fn(tx); err != nil {
        if rerr := tx.Rollback(); rerr != nil {
            err = fmt.Errorf("%w: rolling back transaction: %v", err, rerr)
        }
        return err
    }
    if err := tx.Commit(); err != nil {
        return fmt.Errorf("committing transaction: %w", err)
    }
    return nil
}

使用方法:

func createUser(tx *ent.Tx, u UserData) *ent.UserCreate {
    return tx.User.Create().
        SetName(u.Name).
        SetNillableNickName(u.NickName)
}

func updateUser(tx *ent.Tx, u UserData) *ent.UserUpdate {
    return tx.User.Update().
        Where(
            user.Name(u.Name),
        ).
        SetNillableNickName(u.NickName)
}

func deleteUser(tx *ent.Tx, u UserData) *ent.UserDelete {
    return tx.User.Delete().
        Where(
            user.Name(u.Name),
        )
}

func batchCreateUser(tx *ent.Tx, users []UserData) error {
    userCreates := make([]*ent.UserCreate, 0)
    for _, u := range users {
        userCreates = append(userCreates, createUser(tx, u))
    }
    if _, err := tx.User.CreateBulk(userCreates...).Save(r.ctx); err != nil {
        return err
    }
    return nil
}

func DoBatchCreateUser(ctx context.Context, client *ent.Client) {
    if err := WithTx(ctx, client, func(tx *ent.Tx) error {
        return batchCreateUser(tx, users)
    }); err != nil {
        log.Fatal(err)
    }
}

创建gRPC接口

如果你已经有了数据库的表结构,当你开始初始化一个项目的时候,你不必写任何一行代码,就完成了从ent的数据库定义,到网络API定义的全流程。接着,你需要做的,也就是微调,然后开始撸业务逻辑代码了。不要太开心!现在不都流行所谓的“低代码”吗?这不就是吗!

安装protoc插件:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

go install entgo.io/contrib/entproto/cmd/protoc-gen-entgrpc@latest

向项目添加依赖库:

go get -u entgo.io/contrib/entproto

Schema添加entproto.Message()entproto.Service()方法:

func (User) Annotations() []schema.Annotation {
    return []schema.Annotation{
        entproto.Message(),
        entproto.Service(
            entproto.Methods(entproto.MethodCreate | entproto.MethodGet | entproto.MethodList | entproto.MethodBatchCreate),
        ),
    }
}

其中,entproto.Message()将会导致生成Protobuf的messageentproto.Service()将会导致生成gRPC的service

使用entproto.Field()方法向表字段添加Protobuf的字段索引号:

func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name").
            Unique().
            Annotations(
                entproto.Field(2),
            ),
        field.String("email_address").
            Unique().
            Annotations(
                entproto.Field(3),
            ),
    }
}

generate.go添加entgo.io/contrib/entproto/cmd/entproto命令:

package ent

//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema
//go:generate go run -mod=mod entgo.io/contrib/entproto/cmd/entproto -path ./schema

执行生成命令:

go generate ./...

将会生成以下文件:

ent/proto/entpb
├── entpb.pb.go
├── entpb.proto
├── entpb_grpc.pb.go
├── entpb_user_service.go
└── generate.go

生成的entpb.proto文件生成的内容可能会是这样的:

// Code generated by entproto. DO NOT EDIT.
syntax = "proto3";

package entpb;

option go_package = "ent-grpc-example/ent/proto/entpb";

message User {
  int32 id = 1;

  string user_name = 2;

  string email_address = 3;
}

service UserService {
  rpc Create ( CreateUserRequest ) returns ( User );

  rpc Get ( GetUserRequest ) returns ( User );

  rpc Update ( UpdateUserRequest ) returns ( User );

  rpc Delete ( DeleteUserRequest ) returns ( google.protobuf.Empty );

  rpc List ( ListUserRequest ) returns ( ListUserResponse );

  rpc BatchCreate ( BatchCreateUsersRequest ) returns ( BatchCreateUsersResponse );
}

与Kratos携起手来

官方推荐的包结构是这样的:

|- data  
|- biz  
|- service  
|- server  

那么,我们可以把ent放进data文件夹下面去:

|- data  
|  |- ent  
|- biz  
|- service  
|- server

需要说明的是,项目的结构、命名的规范这些并不在本文阐述的范围之内。并非说非要如此,这个可以根据各自的情况来灵活设计。

我使用这样的项目结构和命名规范,仅仅是为了方便讲清楚如何在Kratos中去引用Ent。

创建数据库客户端

data/data.go文件中添加创建数据库客户端的代码,并将之注入到ProviderSet

package data

// ProviderSet is data providers.
var ProviderSet = wire.NewSet(
    NewEntClient,
    ...
)

// Data .
type Data struct {
    db  *ent.Client
}

// NewEntClient 创建数据库客户端
func NewEntClient(conf *conf.Data, logger log.Logger) *ent.Client {
    l := log.NewHelper(log.With(logger, "module", "ent/data"))

    client, err := ent.Open(
        conf.Database.Driver,
        conf.Database.Source,
    )
    if err != nil {
        l.Fatalf("failed opening connection to db: %v", err)
    }
    // 运行数据库迁移工具
    if true {
        if err := client.Schema.Create(context.Background(), migrate.WithForeignKeys(false)); err != nil {
            l.Fatalf("failed creating schema resources: %v", err)
        }
    }
    return client
}

需要说明的是数据库迁移工具,如果数据库中不存在表,迁移工具会创建一个;如果字段存在改变,迁移工具会对字段进行修改。

创建UseCase

在biz文件夹下创建user.go

package biz

type UserRepo interface {
    List(ctx context.Context, req *pagination.PagingRequest) (*v1.ListUserResponse, error)
    Get(ctx context.Context, req *v1.GetUserRequest) (*v1.User, error)
    Create(ctx context.Context, req *v1.CreateUserRequest) (*v1.User, error)
    Update(ctx context.Context, req *v1.UpdateUserRequest) (*v1.User, error)
    Delete(ctx context.Context, req *v1.DeleteUserRequest) (bool, error)
}

type UserUseCase struct {
    repo UserRepo
    log  *log.Helper
}

func NewUserUseCase(repo UserRepo, logger log.Logger) *UserUseCase {
    l := log.NewHelper(log.With(logger, "module", "user/usecase"))
    return &UserUseCase{repo: repo, log: l}
}

func (uc *UserUseCase) List(ctx context.Context, req *pagination.PagingRequest) (*v1.ListUserResponse, error) {
    return uc.repo.ListUser(ctx, req)
}

func (uc *UserUseCase) Get(ctx context.Context, req *v1.GetUserRequest) (*v1.User, error) {
    return uc.repo.GetUser(ctx, req)
}

func (uc *UserUseCase) Create(ctx context.Context, req *v1.CreateUserRequest) (*v1.User, error) {
    return uc.repo.CreateUser(ctx, req)
}

func (uc *UserUseCase) Update(ctx context.Context, req *v1.UpdateUserRequest) (*v1.User, error) {
    return uc.repo.UpdateUser(ctx, req)
}

func (uc *UserUseCase) Delete(ctx context.Context, req *v1.DeleteUserRequest) (bool, error) {
    return uc.repo.DeleteUser(ctx, req)
}

注入到biz.ProviderSet

package biz

// ProviderSet is biz providers.
var ProviderSet = wire.NewSet(
    NewUserUseCase,
    ...
)

创建Repo

data文件夹下创建user.go文件,实际操作数据库的操作都在此处。

package data

var _ biz.UserRepo = (*UserRepo)(nil)

type UserRepo struct {
    data *Data
    log  *log.Helper
}

func NewUserRepo(data *Data, logger log.Logger) biz.UserRepo {
    l := log.NewHelper(log.With(logger, "module", "User/repo"))
    return &UserRepo{
        data: data,
        log:  l,
    }
}

func (r *userRepo) Delete(ctx context.Context, req *v1.DeleteUserRequest) (bool, error) {
    err := r.data.db.User.
        DeleteOneID(req.GetId()).
        Exec(ctx)
    return err != nil, err
}

...

注入到data.ProviderSet

package data

// ProviderSet is data providers.
var ProviderSet = wire.NewSet(
    NewUserRepo,
    ...
)

在Service中调用

package service

type UserService struct {
    v1.UnimplementedUserServiceServer

    uc  *biz.UserUseCase
    log *log.Helper
}

func NewUserService(logger log.Logger, uc *biz.UserUseCase) *UserService {
    l := log.NewHelper(log.With(logger, "module", "service/user"))
    return &UserService{
        log: l,
        uc:  uc,
    }
}

// ListUser 列表
func (s *UserService) ListUser(ctx context.Context, req *pagination.PagingRequest) (*v1.ListUserResponse, error) {
    return s.uc.List(ctx, req)
}

// GetUser 获取
func (s *UserService) GetUser(ctx context.Context, req *v1.GetUserRequest) (*v1.User, error) {
    return s.uc.Get(ctx, req)
}

// CreateUser 创建
func (s *UserService) CreateUser(ctx context.Context, req *v1.CreateUserRequest) (*v1.User, error) {
    return s.uc.Create(ctx, req)
}

// UpdateUser 更新
func (s *UserService) UpdateUser(ctx context.Context, req *v1.UpdateUserRequest) (*v1.User, error) {
    return s.uc.Update(ctx, req)
}

// DeleteUser 删除
func (s *UserService) DeleteUser(ctx context.Context, req *v1.DeleteUserRequest) (*emptypb.Empty, error) {
    _, err := s.uc.Delete(ctx, req)
    if err != nil {
        return nil, err
    }
    return &emptypb.Empty{}, nil
}

结语

Ent是一个优秀的ORM框架。基于模板进行代码生成,相比较利用反射等方式,在性能上的损耗更少。并且,模板的使用使得扩展系统变得简单容易。

它不仅能够很对传统的关系数据库(MySQL、PostgreSQL、SQLite)方便的进行查询,并且可以容易的进行图遍历——常用的譬如像是:菜单树、组织树……这种数据查询。

Ent的工具链完整。对gRPC和GraphQL也支持的极好,也有相应的一系列工具链进行支持。从数据库表可以用工具转换成Ent的Schema,从Schema可以生成gRPC和GraphQL的API的接口。Kratos的RPC就是基于的gRPC,也支持GraphQL,简直就是为Kratos量身定做的。

相比较其他的ORM框架,Ent对工程化的支持是极佳的,这对于开发维护的效率将会有极大的提升,几个项目下来,受益良多。个人而言,我是极力推崇的。

参考资料

  1. 官方网站: https://entgo.io/
  2. 官方文档: https://entgo.io/zh/docs/getting-started/
  3. 代码仓库: https://github.com/ent/ent
  4. SQL转Schema在线工具: https://printlove.cn/tools/sql2ent
  5. ORM 实例教程 - 阮一峰: http://www.ruanyifeng.com/blog/2019/02/orm-tutorial.html
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
107 3
|
1月前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
2月前
|
Dubbo Java 应用服务中间件
微服务框架Dubbo环境部署实战
微服务框架Dubbo环境部署的实战指南,涵盖了Dubbo的概述、服务部署、以及Dubbo web管理页面的部署,旨在指导读者如何搭建和使用Dubbo框架。
229 17
微服务框架Dubbo环境部署实战
|
2月前
|
Kubernetes Java Android开发
用 Quarkus 框架优化 Java 微服务架构的设计与实现
Quarkus 是专为 GraalVM 和 OpenJDK HotSpot 设计的 Kubernetes Native Java 框架,提供快速启动、低内存占用及高效开发体验,显著优化了 Java 在微服务架构中的表现。它采用提前编译和懒加载技术实现毫秒级启动,通过优化类加载机制降低内存消耗,并支持多种技术和框架集成,如 Kubernetes、Docker 及 Eclipse MicroProfile,助力开发者轻松构建强大微服务应用。例如,在电商场景中,可利用 Quarkus 快速搭建商品管理和订单管理等微服务,提升系统响应速度与稳定性。
70 5
|
2月前
|
存储 Java Maven
从零到微服务专家:用Micronaut框架轻松构建未来架构
【9月更文挑战第5天】在现代软件开发中,微服务架构因提升应用的可伸缩性和灵活性而广受欢迎。Micronaut 是一个轻量级的 Java 框架,适合构建微服务。本文介绍如何从零开始使用 Micronaut 搭建微服务架构,包括设置开发环境、创建 Maven 项目并添加 Micronaut 依赖,编写主类启动应用,以及添加控制器处理 HTTP 请求。通过示例代码展示如何实现简单的 “Hello, World!” 功能,并介绍如何通过添加更多依赖来扩展应用功能,如数据访问、验证和安全性等。Micronaut 的强大和灵活性使你能够快速构建复杂的微服务系统。
95 5
|
2月前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
75 2
|
2月前
|
Cloud Native 安全 Java
Micronaut对决Spring Boot:谁是微服务领域的王者?揭秘两者优劣,选对框架至关重要!
【9月更文挑战第5天】近年来,微服务架构备受关注,Micronaut和Spring Boot成为热门选择。Micronaut由OCI开发,基于注解的依赖注入,内置多种特性,轻量级且启动迅速;Spring Boot则简化了Spring应用开发,拥有丰富的生态支持。选择框架需考虑项目需求、团队经验、性能要求及社区支持等因素。希望本文能帮助您选择合适的微服务框架,助力您的软件开发项目取得成功!
162 2
|
3月前
|
Cloud Native JavaScript API
一文读懂云原生 go-zero 微服务框架
一文读懂云原生 go-zero 微服务框架
|
3月前
|
消息中间件 开发框架 Go
【揭秘】如何让Kratos微服务与NATS消息队列完美融合?看完这篇你就懂了!
【8月更文挑战第22天】Kratos是基于Go语言的微服务框架,提供全面工具助力开发者构建高性能应用。NATS作为轻量级消息队列服务,适用于分布式系统消息传递。本文详细介绍如何在Kratos项目中集成NATS,包括创建项目、安装NATS客户端、配置连接、初始化NATS、发送与接收消息等步骤,助您轻松实现高效微服务架构。
68 1
|
3月前
|
开发框架 Dubbo 应用服务中间件
微服务开发框架-----Apache Dubbo
这篇文章介绍了Apache Dubbo微服务开发框架,它提供RPC通信和微服务治理能力,支持服务发现、负载均衡和流量治理等功能,并强调了Dubbo在微服务规模化实践和企业级治理方面的优势。
微服务开发框架-----Apache Dubbo