采用gRPC、Spring Boot、Kotlin和MySQL开发的SNS应用(包含通话和聊天)的API样例
这个应用程序的API是什么?
-
- チャット、通話機能があるSNSアプリのAPIです。
- ユーザー登録、プッシュ通知、ブロック・通報機能などのsnsアプリに必要な一通りのAPIを実装しています。
系统的构成
-
- フレームワーク:Spring Boot
-
- 言語:Kotlin
-
- API:Protocol Buffersで定義、gRPCで実装。
- DB:MySQL
– 代码 (Daima)
评论 huà
)
-
- gRPC, Protocol Buffersがどんなものかは他に沢山情報があるので省略します。
-
- 画像ファイルのアップロードはこのサンプルに含みません。
- iOSプッシュ通知の用のp8ファイルや、その他の認証用の文字列は無効なものになっています。
API清单
账号
-
- ユーザー登録
-
- プロフィール編集
- 退会
用户信息:
-
- ユーザーのリスト取得
- 指定したidのユーザーを取得
打电话
-
- 通話を開始。(電話を掛ける)
-
- 通話を受ける。
-
- 通話を切る。
-
- 通話が継続していることを記録。
- 通話のステータス受信(開始、通話中、終了)
聊天
-
- メッセージ送信
-
- メッセージ受信
- すべてのメッセージを取得
推播通知
-
- デバイストークン_iOS / 登録, 更新
- ON/OFF更新
其他
-
- ユーザーをブロック
- ユーザーを通報
使用Protocol Buffers进行API规范定义。
-
- account.proto
-
- block.proto
-
- calling.proto
-
- chat.proto
-
- commons.proto
-
- push_notify.proto
-
- report.proto
- user.proto
gRPC的控制器类(API)
-
- AccountGrpcService
-
- AccountRegisterGrpcService
-
- BlockGrpcService
-
- CallingGrpcService
-
- ChatGrpcService
-
- PushNotifyGrpcService
-
- ReportGrpcService
- UserGrpcService
在 build.gradle 中,从 proto 文件生成 API 的源码。
group 'com.talking'
// gRPCのコード生成コマンド: ./gradlew generateProto
// ドキュメント: gRPCの実装
// https://github.com/LogNet/grpc-spring-boot-starter
buildscript {
ext.kotlin_version = '1.3.61' // Required for Kotlin integration
ext.spring_boot_version = '2.2.0.RELEASE'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // Required for Kotlin integration
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version" // See https://kotlinlang.org/docs/reference/compiler-plugins.html#spring-support
classpath "org.springframework.boot:spring-boot-gradle-plugin:$spring_boot_version"
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.5"
}
}
apply plugin: 'kotlin' // Required for Kotlin integration
apply plugin: "kotlin-spring" // https://kotlinlang.org/docs/reference/compiler-plugins.html#spring-support
apply plugin: 'org.springframework.boot'
apply plugin: 'com.google.protobuf'
def grpcVersion = '1.25.0'
repositories {
mavenCentral()
}
sourceSets {
main.kotlin.srcDirs += 'src/main/kotlin'
main.java.srcDirs += 'src/main/java'
main.java.srcDirs += 'src/main/generated-proto'
}
dependencies {
compile("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
compile("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
// testCompile('org.springframework.boot:spring-boot-starter-test')
// for web
compile("org.springframework.boot:spring-boot-starter-web:$spring_boot_version")
compile("com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8")
// for db
compile("org.springframework.boot:spring-boot-starter-data-jpa:$spring_boot_version")
runtime("mysql:mysql-connector-java:8.0.15")
compile("org.springframework.boot:spring-boot-starter-security:$spring_boot_version")
// grpc
compile("io.github.lognet:grpc-spring-boot-starter:3.5.0")
compile "io.grpc:grpc-api:${grpcVersion}"
compile "io.netty:netty-codec-http2:4.1.42.Final"
compile "io.grpc:grpc-core:${grpcVersion}"
compile "io.grpc:grpc-protobuf:${grpcVersion}"
compile "io.grpc:grpc-stub:${grpcVersion}"
// cache
compile(group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '2.8.1')
// iOSプッシュ通知
compile(group: 'com.eatthepath', name: 'pushy', version: '0.13.11')
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.5.0"
}
plugins {
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
outputSubDir = 'generated-proto'
}
}
task.plugins {
grpc {
outputSubDir = 'generated-proto'
}
}
}
}
generatedFilesBaseDir = "$projectDir/src/"
}
截止日期
桌子
-
- ユーザー
-
- プロフィール画像
-
- チャット
-
- 通話履歴
-
- 通報
-
- ブロック
- プッシュ通知デバイストークン_iOS
努力了一下
将仅允许登录用户使用的API认证过程进行共通化处理。
在AuthInterceptor中使用request头部中的token和userId进行身份验证。
@Component
class AuthInterceptor : ServerInterceptor {
@Autowired
private lateinit var userService: UserService
// 参考ソース:
// https://github.com/saturnism/grpc-by-example-java/blob/master/metadata-context-example/src/main/java/com/example/grpc/server/JwtServerInterceptor.java
override fun <ReqT : Any?, RespT : Any?> interceptCall(
call: ServerCall<ReqT, RespT>,
headers: Metadata,
next: ServerCallHandler<ReqT, RespT>
): ServerCall.Listener<ReqT> {
val userId = headers.get(GrpcConstant.USER_ID_METADATA_KEY)?.toLong() ?:
throw StatusRuntimeException(Status.UNAUTHENTICATED, headers)
val apiToken = headers.get(GrpcConstant.API_TOKEN_METADATA_KEY) ?:
throw StatusRuntimeException(Status.UNAUTHENTICATED, headers)
val user = userService.findByIdAndToken(userId, apiToken) ?:
throw StatusRuntimeException(Status.UNAUTHENTICATED, headers)
val ctx = Context.current().withValue(GrpcConstant.AUTH_USER_CONTEXT_KEY, user)
return Contexts.interceptCall(ctx, call, headers, next)
}
}
在想要使用认证处理的API类中设置AuthInterceptor。
https://github.com/yusuke-imagawa/talking-sns-api-sample/blob/master/src/main/kotlin/com/talking/api/grpc/AccountGrpcService.kt
@GRpcService(interceptors = [AuthInterceptor::class])
class AccountGrpcService: AccountServiceGrpc.AccountServiceImplBase() {