1、搭建Quarkus框架
mvn io.quarkus:quarkus-maven-plugin:create \
-DprojectGroupId=com.quarkus \
-DprojectArtifactId=grpc-kotlin \
-DprojectVersion=1.0 \
-DclassName=StockResource \
-Dpath=stock
quarkus.platform.version 改为3.6.8
添加kotlin支持
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-kotlin</artifactId>
</dependency>
添加3个module:grpc-proto、grpc-server、grpc-client
<modules>
<module>grpc-proto</module>
<module>grpc-server</module>
<module>grpc-client</module>
</modules>
2、搭建Grpc环境
1)在grpc-proto module中,引入quarkus-grpc,创建proto文件
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-grpc</artifactId>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/proto</directory>
<targetPath>proto</targetPath>
</resource>
</resources>
</build>
syntax = "proto3";
package model;
option java_package = "com.quarkus.grpc.model";
option java_outer_classname = "StockProto";
service StockService {
rpc getQuantity(Stock) returns (Stocks) {}
}
message Stock {
int64 id = 1;
string stockDate = 2;
int32 storeCode = 3;
string productCode = 4;
double quantity = 5;
string operationTime = 6;
string transferTime = 7;
string registeredAt = 8;
string registeredBy = 9;
}
message Stocks {
repeated Stock stock = 1;
}
grpc-proto目录下运行命令:mvn install
2)在gprc-server模块中,引入postgresql、jpa支持
<dependency>
<groupId>com.yunyan</groupId>
<artifactId>quarkus-grpc-proto</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-data-jpa</artifactId>
</dependency>
配置端口号,数据库连接、jpa命名策略
quarkus.http.port=8082
quarkus.grpc.server.port=9092
quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = postgres
quarkus.datasource.password = postgre
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/postgres
quarkus.hibernate-orm.physical-naming-strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
quarkus.hibernate-orm.implicit-naming-strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy
quarkus.hibernate-orm.log.sql=true
创建Stock类
@Entity
@Table(name = "init_stock")
open class Stock {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0
var stockDate: LocalDateTime? = null
var storeCode: Int? = null
var productCode: String? = null
var quantity: BigDecimal? = null
var operationTime: LocalDateTime? = null
var transferTime: LocalDateTime? = null
var registeredAt: LocalDateTime? = null
var registeredBy: String? = null
}
StockRepository类
interface StockRepository : JpaRepository<Stock, Long> {
fun findByStoreCodeAndStockDateAndProductCode(
storeCode: Int,
stockDate: LocalDateTime,
productCode: String
): List<Stock>
}
StockServiceImpl类
@Blocking
@GrpcService
class StockServiceImpl : StockService {
@Inject
lateinit var stockRepository: StockRepository
override fun getQuantity(request: StockProto.Stock): Uni<Stocks> {
val stockList = stockRepository.findByStoreCodeAndStockDateAndProductCode(
request.storeCode,
LocalDate.parse(request.stockDate).atStartOfDay(),
request.productCode
)
val stockMap: MutableMap<String, Stock> = HashMap(16)
stockList.forEach { t: Stock ->
val key = t.storeCode.toString() + t.stockDate.toString() + t.productCode
if (stockMap.containsKey(key)) {
val stock = stockMap[key]
stock!!.quantity = stock.quantity!!.add(t.quantity)
} else {
stockMap.put(key, t)
}
}
val stocks = stockMap.values.stream().collect(Collectors.toList())
return Uni.createFrom().item(stocks).map { stocks: List<Stock> -> toProtoStocks(stocks) }
}
private fun toProtoStocks(stocks: List<Stock>): Stocks {
val builder = Stocks.newBuilder()
stocks.forEach { t: Stock -> builder.addStock(toProtoStock(t)) }
return builder.build()
}
private fun toProtoStock(stock: Stock): StockProto.Stock {
val builder = StockProto.Stock.newBuilder()
builder.setStoreCode(stock.storeCode!!)
.setStockDate(stock.stockDate!!.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")))
.setProductCode(stock.productCode).quantity = stock.quantity!!.toDouble()
return builder.build()
}
}
运行grpc-server:mvn quarkus:dev
进入http://localhost:8082/q/dev-ui,点击Extensions、gRPC Services显示如下页面:

image.png
3)grpc-client端创建
pom文件中引入如下包:
<dependency>
<groupId>com.quarkus</groupId>
<artifactId>grpc-proto</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-mutiny</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
</dependency>
配置端口:
quarkus.http.port=8081
quarkus.grpc.server.port=9091
quarkus.grpc.clients.stockService.port=9092
测试代码:
@Path("/stock")
class StockResource {
@GrpcClient
lateinit var stockService: StockService
@POST
@Path("/getQuantity")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
fun getQuantity(model: Model): Uni<List<Model>> {
val builder: StockProto.Stock.Builder = StockProto.Stock.newBuilder()
builder.setStockDate(model.stockDate)
.setStoreCode(model.stockCode).productCode = model.productCode
val stocks: Uni<StockProto.Stocks> = stockService.getQuantity(builder.build())
return stocks.onItem().transform { stocks: StockProto.Stocks -> transToModel(stocks) }
}
private fun transToModel(stocks: StockProto.Stocks): List<Model> {
val list: MutableList<Model> = ArrayList()
stocks.stockList.forEach { t: StockProto.Stock ->
var model = Model()
model.stockCode = t.storeCode
model.stockDate = t.stockDate
model.productCode = t.productCode
model.quantity = t.quantity
list.add(model)
}
return list
}
}
数据库数据:

image.png
运行grpc-client:mvn quarkus:dev ,postman调用结果如下:

image.png