企业级项目开发流程
1. 项目初始化策略
mkdir enterprise-app && cd enterprise-app
jhipster
Application type: Monolithic application
Base name: enterpriseApp
Package name: com.enterprise.app
Build tool: Maven
Authentication: JWT
Database: PostgreSQL
Cache: Redis
2. 团队协作配置
{
"generator-jhipster" : {
"applicationType" : "monolith" ,
"baseName" : "enterpriseApp" ,
"packageName" : "com.enterprise.app" ,
"buildTool" : "maven" ,
"authenticationType" : "jwt" ,
"databaseType" : "sql" ,
"devDatabaseType" : "postgresql" ,
"prodDatabaseType" : "postgresql" ,
"cacheProvider" : "redis" ,
"enableHibernateCache" : true ,
"websocket" : true ,
"searchEngine" : "elasticsearch"
}
}
3. 代码质量配置
npm run lint
npm run test:ci
./mvnw sonar:sonar \
-Dsonar.projectKey = enterprise-app \
-Dsonar.host.url = http://localhost:9000
微服务架构实战
1. 架构设计
API网关:8080
用户服务:8081
订单服务:8082
库存服务:8083
用户数据库
订单数据库
库存数据库
服务注册中心:8761
2. JDL微服务定义
// microservices.jdl
application {
config {
baseName gateway
applicationType gateway
packageName com.enterprise.gateway
serverPort 8080
serviceDiscoveryType consul
authenticationType jwt
databaseType sql
prodDatabaseType postgresql
clientFramework angular
}
}
application {
config {
baseName userservice
applicationType microservice
packageName com.enterprise.user
serverPort 8081
serviceDiscoveryType consul
authenticationType jwt
databaseType sql
prodDatabaseType postgresql
skipClient true
}
entities User, Role
}
application {
config {
baseName orderservice
applicationType microservice
packageName com.enterprise.order
serverPort 8082
serviceDiscoveryType consul
authenticationType jwt
databaseType sql
prodDatabaseType postgresql
skipClient true
}
entities Order, OrderItem
}
// 实体定义
entity User {
login String required unique
email String required unique
firstName String
lastName String
activated Boolean
}
entity Role {
name String required unique
}
entity Order {
orderDate Instant required
status OrderStatus required
totalAmount BigDecimal required min(0)
}
entity OrderItem {
quantity Integer required min(1)
unitPrice BigDecimal required min(0)
totalPrice BigDecimal required min(0)
}
enum OrderStatus {
PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED
}
// 关系定义
relationship ManyToMany {
User{roles} to Role{users}
}
relationship OneToMany {
User{orders} to Order{customer}
Order{items} to OrderItem{order}
}
// 服务分配
microservice User, Role with userservice
microservice Order, OrderItem with orderservice
3. 微服务生成与配置
jhipster jdl microservices.jdl
cd gateway && ./mvnw spring-boot:run
cd .. /userservice && ./mvnw spring-boot:run
cd .. /orderservice && ./mvnw spring-boot:run
JDL高级建模
1. 复杂业务建模
// 电商系统完整建模
application {
config {
baseName ecommerce
applicationType monolith
packageName com.ecommerce.app
databaseType sql
prodDatabaseType postgresql
clientFramework angular
enableSwaggerCodegen true
}
}
// 核心业务实体
entity Customer {
customerCode String required unique pattern(/^[A-Z]{2}\d{6}$/)
firstName String required minlength(2) maxlength(50)
lastName String required minlength(2) maxlength(50)
email String required unique pattern(/^[^@\s]+@[^@\s]+\.[^@\s]+$/)
phoneNumber String pattern(/^\+?[1-9]\d{1,14}$/)
dateOfBirth LocalDate
gender Gender
registrationDate Instant required
isActive Boolean
}
entity Product {
sku String required unique minlength(3) maxlength(20)
name String required minlength(2) maxlength(100)
description TextBlob
price BigDecimal required min(0)
discountPrice BigDecimal min(0)
stockQuantity Integer required min(0)
isActive Boolean
weight Double min(0)
dimensions String
createdDate Instant required
lastModifiedDate Instant
}
entity Category {
name String required unique minlength(2) maxlength(50)
description String maxlength(500)
categoryCode String required unique
isActive Boolean
sortOrder Integer
}
entity Order {
orderNumber String required unique pattern(/^ORD-\d{8}-\d{4}$/)
orderDate Instant required
deliveryDate Instant
status OrderStatus required
totalAmount BigDecimal required min(0)
discountAmount BigDecimal min(0)
taxAmount BigDecimal min(0)
shippingAmount BigDecimal min(0)
paymentMethod PaymentMethod required
notes TextBlob
}
entity OrderItem {
quantity Integer required min(1)
unitPrice BigDecimal required min(0)
totalPrice BigDecimal required min(0)
discountAmount BigDecimal min(0)
}
entity Address {
type AddressType required
street String required maxlength(200)
city String required maxlength(50)
state String maxlength(50)
postalCode String required pattern(/^\d{5}(-\d{4})?$/)
country String required maxlength(50)
isDefault Boolean
}
entity Payment {
paymentDate Instant required
amount BigDecimal required min(0)
paymentMethod PaymentMethod required
transactionId String unique
status PaymentStatus required
notes String maxlength(500)
}
// 枚举定义
enum Gender {
MALE, FEMALE, OTHER
}
enum OrderStatus {
DRAFT, PENDING, CONFIRMED, PROCESSING, SHIPPED, DELIVERED, CANCELLED, RETURNED
}
enum PaymentMethod {
CREDIT_CARD, DEBIT_CARD, PAYPAL, BANK_TRANSFER, CASH_ON_DELIVERY
}
enum PaymentStatus {
PENDING, COMPLETED, FAILED, REFUNDED, CANCELLED
}
enum AddressType {
HOME, WORK, SHIPPING, BILLING
}
// 复杂关系定义
relationship OneToMany {
Customer{orders} to Order{customer required}
Customer{addresses} to Address{customer required}
Order{items} to OrderItem{order required}
Order{payments} to Payment{order required}
Category{products} to Product{category}
Product{orderItems} to OrderItem{product required}
}
relationship ManyToOne {
Order{shippingAddress} to Address
Order{billingAddress} to Address
}
// 分页配置
paginate Customer, Product, Order with pagination
paginate Category with infinite-scroll
// 服务层生成
service Customer, Product, Order with serviceImpl
service Category, Address with serviceClass
// DTO配置
dto Customer, Product, Order, OrderItem with mapstruct
// 搜索配置
search Customer, Product with elasticsearch
// 微服务部署选项
deployment {
deploymentType docker-compose
monitoring prometheus
serviceDiscoveryType consul
dockerRepositoryName "mycompany"
enableRancherLoadBalancing true
}
2. JDL验证与最佳实践
const validateJDL = async ( jdlContent: string ) => {
const validator = {
validateEntityNames : ( entities: Entity[ ] ) => {
entities. forEach ( entity => {
if ( ! / ^[A-Z][a-zA-Z0-9]*$ / . test ( entity. name) ) {
throw new Error ( ` 实体名称格式错误: ${ entity. name} ` ) ;
}
} ) ;
} ,
validateRelationships : ( relationships: Relationship[ ] ) => {
relationships. forEach ( rel => {
if ( rel. type === 'OneToMany' && ! rel. otherEntityField) {
console . warn ( ` 建议为 ${ rel. from} -> ${ rel. to} 关系设置外键字段 ` ) ;
}
} ) ;
} ,
validateConstraints : ( entities: Entity[ ] ) => {
entities. forEach ( entity => {
entity. fields. forEach ( field => {
if ( field. type === 'String' && ! field. validation?. maxlength) {
console . warn ( ` 建议为 ${ entity. name} . ${ field. name} 设置最大长度 ` ) ;
}
} ) ;
} ) ;
}
} ;
return validator;
} ;
实体关系设计
1. 高级关系映射
// 复杂关系设计示例
entity BlogPost {
title String required maxlength(100)
content TextBlob required
publishDate Instant
isPublished Boolean
viewCount Long
slug String required unique
}
entity Tag {
name String required unique maxlength(30)
color String pattern(/^#[0-9A-Fa-f]{6}$/)
}
entity Comment {
content TextBlob required
commentDate Instant required
isApproved Boolean
authorName String required maxlength(50)
authorEmail String required
}
entity User {
username String required unique minlength(3) maxlength(50)
email String required unique
firstName String maxlength(50)
lastName String maxlength(50)
}
// 多对多关系(文章-标签)
relationship ManyToMany {
BlogPost{tags} to Tag{posts}
}
// 层级关系(评论回复)
relationship OneToMany {
Comment{replies} to Comment{parent}
BlogPost{comments} to Comment{post required}
User{posts} to BlogPost{author required}
User{comments} to Comment{commenter required}
}
// 自引用关系示例
entity Category {
name String required
description String
level Integer
sortOrder Integer
}
relationship OneToMany {
Category{children} to Category{parent}
}
2. 数据库索引优化
@Entity
@Table ( name = "blog_post" , indexes = {
@Index ( name = "idx_blog_post_slug" , columnList = "slug" ) ,
@Index ( name = "idx_blog_post_published" , columnList = "is_published" ) ,
@Index ( name = "idx_blog_post_publish_date" , columnList = "publish_date" ) ,
@Index ( name = "idx_blog_post_author" , columnList = "author_id" )
} )
public class BlogPost {
@Column ( name = "slug" , unique = true , nullable = false )
private String slug;
@Column ( name = "is_published" )
private Boolean isPublished;
@Column ( name = "publish_date" )
private Instant publishDate;
@ManyToOne ( fetch = FetchType . LAZY )
@JoinColumn ( name = "author_id" )
private User author;
}
蓝图定制开发
1. 创建自定义蓝图
jhipster generate-blueprint
Blueprint name: enterprise
Description: Enterprise features blueprint
Local blueprint: Yes
CLI: Yes
2. 蓝图生成器实现
import BaseApplicationGenerator from 'generator-jhipster/generators/base-application' ;
export default class extends BaseApplicationGenerator {
constructor ( args, opts, features) {
super ( args, opts, features) ;
}
get [ BaseApplicationGenerator. INITIALIZING ] ( ) {
return this . asInitializingTaskGroup ( {
async parseBlueprint ( ) {
await this . parseCurrentJHipsterCommand ( ) ;
}
} ) ;
}
get [ BaseApplicationGenerator. PROMPTING ] ( ) {
return this . asPromptingTaskGroup ( {
async askForEnterpriseFeatures ( ) {
const answers = await this . prompt ( [
{
type: 'confirm' ,
name: 'enableAudit' ,
message: '启用审计功能?' ,
default : true
} ,
{
type: 'confirm' ,
name: 'enableMultiTenant' ,
message: '启用多租户?' ,
default : false
} ,
{
type: 'list' ,
name: 'loggingFramework' ,
message: '选择日志框架' ,
choices: [ 'logback' , 'log4j2' ] ,
default : 'logback'
}
] ) ;
this . blueprintStorage. set ( answers) ;
}
} ) ;
}
get [ BaseApplicationGenerator. WRITING ] ( ) {
return this . asWritingTaskGroup ( {
async writeEnterpriseFiles ( ) {
if ( this . blueprintStorage. get ( 'enableAudit' ) ) {
await this . writeFiles ( {
sections: {
auditFiles: [
{
templates: [
'src/main/java/_package_/config/audit/AuditConfiguration.java.ejs' ,
'src/main/java/_package_/config/audit/AuditEventConverter.java.ejs'
]
}
]
}
} ) ;
}
if ( this . blueprintStorage. get ( 'enableMultiTenant' ) ) {
await this . writeFiles ( {
sections: {
multiTenantFiles: [
{
templates: [
'src/main/java/_package_/config/tenant/TenantContext.java.ejs' ,
'src/main/java/_package_/config/tenant/TenantInterceptor.java.ejs'
]
}
]
}
} ) ;
}
}
} ) ;
}
}
3. 蓝图模板定制
package < %= packageName % > . config. audit;
import org. springframework. context. annotation. Bean ;
import org. springframework. context. annotation. Configuration ;
import org. springframework. data. domain. AuditorAware ;
import org. springframework. data. jpa. repository. config. EnableJpaAuditing ;
import org. springframework. security. core. Authentication ;
import org. springframework. security. core. context. SecurityContext ;
import org. springframework. security. core. context. SecurityContextHolder ;
import java. util. Optional ;
@Configuration
@EnableJpaAuditing ( auditorAwareRef = "auditorProvider" )
public class AuditConfiguration {
@Bean
public AuditorAware < String > auditorProvider ( ) {
return ( ) -> Optional . ofNullable ( SecurityContextHolder . getContext ( ) )
. map ( SecurityContext :: getAuthentication )
. filter ( Authentication :: isAuthenticated )
. map ( Authentication :: getName ) ;
}
}
CI/CD集成配置
1. GitHub Actions配置
name : CI/CD Pipeline
on :
push :
branches : [ main, develop ]
pull_request :
branches : [ main ]
jobs :
test :
runs-on : ubuntu- latest
services :
postgres :
image : postgres: 13
env :
POSTGRES_PASSWORD : postgres
POSTGRES_DB : testdb
options : > -
- - health- cmd pg_isready
- - health- interval 10s
- - health- timeout 5s
- - health- retries 5
steps :
- uses : actions/checkout@v3
- name : Setup Node.js
uses : actions/setup- node@v3
with :
node-version : '18'
cache : 'npm'
- name : Setup Java
uses : actions/setup- java@v3
with :
java-version : '17'
distribution : 'temurin'
- name : Cache Maven dependencies
uses : actions/cache@v3
with :
path : ~/.m2
key : ${ { runner.os } } - m2- ${ { hashFiles('**/pom.xml') } }
- name : Install dependencies
run : npm ci
- name : Run backend tests
run : ./mvnw clean verify
- name : Run frontend tests
run : npm test
- name : Build application
run : ./mvnw clean package - Pprod
- name : Build Docker image
run : |
docker build -t ${{ github.repository }}:${{ github.sha }} .
- name : Run security scan
uses : securecodewarrior/github- action- add- sarif@v1
with :
sarif-file : 'security-scan-results.sarif'
deploy :
needs : test
runs-on : ubuntu- latest
if : github.ref == 'refs/heads/main'
steps :
- uses : actions/checkout@v3
- name : Deploy to staging
run : |
# Kubernetes deployment
kubectl apply -f k8s/
2. Jenkins Pipeline配置
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'your-registry.com'
IMAGE_NAME = 'enterprise-app'
KUBECONFIG = credentials ( 'kubeconfig' )
}
stages {
stage ( 'Checkout' ) {
steps {
checkout scm
}
}
stage ( 'Test' ) {
parallel {
stage ( 'Backend Tests' ) {
steps {
sh './mvnw clean test'
}
post {
always {
publishTestResults testsPattern: 'target/surefire-reports/*.xml'
}
}
}
stage ( 'Frontend Tests' ) {
steps {
sh 'npm test -- --watchAll=false --coverage'
}
post {
always {
publishCoverage adapters: [
coberturaAdapter ( 'coverage/cobertura-coverage.xml' )
]
}
}
}
}
}
stage ( 'Quality Gate' ) {
steps {
withSonarQubeEnv ( 'SonarQube' ) {
sh './mvnw sonar:sonar'
}
timeout ( time: 10 , unit: 'MINUTES' ) {
waitForQualityGate abortPipeline: true
}
}
}
stage ( 'Build' ) {
steps {
sh './mvnw clean package -Pprod'
script {
def image = docker. build ( " ${ DOCKER_REGISTRY } / ${ IMAGE_NAME } : ${ BUILD_NUMBER } " )
docker. withRegistry ( "https:// ${ DOCKER_REGISTRY } " , 'docker-registry-credentials' ) {
image. push ( )
image. push ( 'latest' )
}
}
}
}
stage ( 'Deploy' ) {
when {
branch 'main'
}
steps {
sh """
helm upgrade --install enterprise-app ./helm-chart \
--set image.tag= ${ BUILD_NUMBER } \
--set database.password= ${ env. DB_PASSWORD } \
--namespace production
"""
}
}
}
post {
failure {
emailext (
subject: "Build Failed: ${ env. JOB_NAME } - ${ env. BUILD_NUMBER } " ,
body: "Build failed. Check console output at ${ env. BUILD_URL } " ,
to: " ${ env. CHANGE_AUTHOR_EMAIL } "
)
}
}
}
3. GitLab CI/CD配置
stages :
- test
- build
- security
- deploy
variables :
DOCKER_DRIVER : overlay2
MAVEN_OPTS : "-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository"
cache :
paths :
- .m2/repository/
- node_modules/
test:backend :
stage : test
image : openjdk: 17- jdk
services :
- postgres: 13
variables :
POSTGRES_DB : testdb
POSTGRES_USER : postgres
POSTGRES_PASSWORD : postgres
script :
- ./mvnw clean test
artifacts :
reports :
junit :
- target/surefire- reports/*.xml
paths :
- target/
test:frontend :
stage : test
image : node: 18
script :
- npm ci
- npm run test: ci
coverage : '/Lines\s*:\s*(\d+\.?\d*)%/'
artifacts :
reports :
coverage_report :
coverage_format : cobertura
path : coverage/cobertura- coverage.xml
security:scan :
stage : security
image : owasp/dependency- check- cli: latest
script :
- dependency- check - - project "Enterprise App" - - scan . - - format JSON - - out dependency- check- report.json
artifacts :
reports :
dependency_scanning : dependency- check- report.json
build:image :
stage : build
image : docker: latest
services :
- docker: dind
script :
- docker build - t $CI_REGISTRY_IMAGE: $CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE: $CI_COMMIT_SHA
only :
- main
- develop
deploy:staging :
stage : deploy
image : bitnami/kubectl: latest
script :
- kubectl config use- context $STAGING_CONTEXT
- kubectl set image deployment/enterprise- app app=$CI_REGISTRY_IMAGE: $CI_COMMIT_SHA
environment :
name : staging
url : https: //staging.enterprise- app.com
only :
- develop
deploy:production :
stage : deploy
image : bitnami/kubectl: latest
script :
- kubectl config use- context $PRODUCTION_CONTEXT
- kubectl set image deployment/enterprise- app app=$CI_REGISTRY_IMAGE: $CI_COMMIT_SHA
environment :
name : production
url : https: //enterprise- app.com
when : manual
only :
- main
生产环境部署
1. Docker化部署
# 多阶段构建Dockerfile
FROM node:18-alpine AS frontend-build
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build:prod
FROM maven:3.8.6-openjdk-17 AS backend-build
WORKDIR /app
COPY pom.xml .
COPY src ./src
COPY --from=frontend-build /app/target/classes/static ./src/main/resources/static
RUN mvn clean package -Pprod -DskipTests
FROM openjdk:17-jre-slim
VOLUME /tmp
EXPOSE 8080
# 安全用户
RUN groupadd -r appuser && useradd --no-log-init -r -g appuser appuser
USER appuser
COPY --from=backend-build /app/target/*.jar app.jar
# JVM优化参数
ENV JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app.jar"]
2. Kubernetes部署配置
apiVersion : apps/v1
kind : Deployment
metadata :
name : enterprise- app
namespace : production
spec :
replicas : 3
strategy :
type : RollingUpdate
rollingUpdate :
maxUnavailable : 1
maxSurge : 1
selector :
matchLabels :
app : enterprise- app
template :
metadata :
labels :
app : enterprise- app
version : "v1.0"
spec :
containers :
- name : app
image : your- registry.com/enterprise- app: latest
ports :
- containerPort : 8080
env :
- name : SPRING_PROFILES_ACTIVE
value : "prod"
- name : DATABASE_URL
valueFrom :
secretKeyRef :
name : db- credentials
key : url
- name : DATABASE_PASSWORD
valueFrom :
secretKeyRef :
name : db- credentials
key : password
resources :
requests :
memory : "512Mi"
cpu : "250m"
limits :
memory : "1Gi"
cpu : "500m"
livenessProbe :
httpGet :
path : /management/health
port : 8080
initialDelaySeconds : 30
periodSeconds : 10
readinessProbe :
httpGet :
path : /management/health/readiness
port : 8080
initialDelaySeconds : 10
periodSeconds : 5
volumeMounts :
- name : app- config
mountPath : /config
readOnly : true
volumes :
- name : app- config
configMap :
name : enterprise- app- config
---
apiVersion : v1
kind : Service
metadata :
name : enterprise- app- service
namespace : production
spec :
selector :
app : enterprise- app
ports :
- protocol : TCP
port : 80
targetPort : 8080
type : ClusterIP
---
apiVersion : networking.k8s.io/v1
kind : Ingress
metadata :
name : enterprise- app- ingress
namespace : production
annotations :
kubernetes.io/ingress.class : "nginx"
cert-manager.io/cluster-issuer : "letsencrypt-prod"
nginx.ingress.kubernetes.io/rate-limit : "100"
spec :
tls :
- hosts :
- enterprise- app.com
secretName : enterprise- app- tls
rules :
- host : enterprise- app.com
http :
paths :
- path : /
pathType : Prefix
backend :
service :
name : enterprise- app- service
port :
number : 80
3. Helm Chart配置
replicaCount : 3
image :
repository : your- registry.com/enterprise- app
tag : "latest"
pullPolicy : IfNotPresent
service :
type : ClusterIP
port : 80
targetPort : 8080
ingress :
enabled : true
annotations :
kubernetes.io/ingress.class : nginx
cert-manager.io/cluster-issuer : letsencrypt- prod
hosts :
- host : enterprise- app.com
paths :
- path : /
pathType : Prefix
tls :
- secretName : enterprise- app- tls
hosts :
- enterprise- app.com
resources :
limits :
cpu : 500m
memory : 1Gi
requests :
cpu : 250m
memory : 512Mi
autoscaling :
enabled : true
minReplicas : 3
maxReplicas : 10
targetCPUUtilizationPercentage : 80
targetMemoryUtilizationPercentage : 80
database :
host : postgres- service
port : 5432
name : enterprise_db
username : enterprise_user
redis :
enabled : true
host : redis- service
port : 6379
monitoring :
enabled : true
serviceMonitor :
enabled : true
interval : 30s
性能优化策略
1. 数据库优化
spring:
datasource:
hikari:
connection- timeout: 20000
minimum- idle: 10
maximum- pool- size: 20
idle- timeout: 300000
max- lifetime: 1200000
auto- commit: false
jpa:
properties:
hibernate:
jdbc:
batch_size: 25
order_inserts: true
order_updates: true
batch_versioned_data: true
connection:
provider_disables_autocommit: true
cache:
use_second_level_cache: true
use_query_cache: true
region:
factory_class: org. hibernate. cache. jcache. JCacheRegionFactory
@Entity
@Table ( name = "product" )
@NamedEntityGraph (
name = "Product.withCategoryAndReviews" ,
attributeNodes = {
@NamedAttributeNode ( "category" ) ,
@NamedAttributeNode ( value = "reviews" , subgraph = "reviews-subgraph" )
} ,
subgraphs = {
@NamedSubgraph (
name = "reviews-subgraph" ,
attributeNodes = { @NamedAttributeNode ( "author" ) }
)
}
)
public class Product {
}
@Repository
public interface ProductRepository extends JpaRepository < Product , Long > {
@Query ( "SELECT p FROM Product p WHERE p.category.id = :categoryId AND p.isActive = true" )
@EntityGraph ( value = "Product.withCategoryAndReviews" )
List < Product > findActiveByCategoryId ( @Param ( "categoryId" ) Long categoryId) ;
@Query ( value = "SELECT * FROM product p WHERE MATCH(p.name, p.description) AGAINST (?1 IN NATURAL LANGUAGE MODE)" ,
nativeQuery = true )
List < Product > findByFullTextSearch ( String searchTerm) ;
}
2. 缓存策略
@Configuration
@EnableCaching
public class CacheConfiguration {
@Bean
public RedisCacheManager cacheManager ( RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration . defaultCacheConfig ( )
. entryTtl ( Duration . ofMinutes ( 30 ) )
. serializeKeysWith ( RedisSerializationContext. SerializationPair . fromSerializer ( new StringRedisSerializer ( ) ) )
. serializeValuesWith ( RedisSerializationContext. SerializationPair . fromSerializer ( new GenericJackson2JsonRedisSerializer ( ) ) ) ;
return RedisCacheManager . builder ( connectionFactory)
. cacheDefaults ( config)
. transactionAware ( )
. build ( ) ;
}
}
@Service
@Transactional
public class ProductService {
@Cacheable ( value = "products" , key = "#categoryId + '_' + #pageable.pageNumber" )
public Page < ProductDTO > findByCategory ( Long categoryId, Pageable pageable) {
return productRepository. findActiveByCategoryId ( categoryId, pageable)
. map ( productMapper:: toDto ) ;
}
@CacheEvict ( value = "products" , allEntries = true )
public ProductDTO save ( ProductDTO productDTO) {
Product product = productMapper. toEntity ( productDTO) ;
product = productRepository. save ( product) ;
return productMapper. toDto ( product) ;
}
}
3. 前端性能优化
@ Component ( {
selector: 'app-product-list' ,
templateUrl: './product-list.component.html' ,
changeDetection: ChangeDetectionStrategy. OnPush,
standalone: true ,
imports: [ CommonModule, RouterModule]
} )
export class ProductListComponent implements OnInit {
products$ = new BehaviorSubject< Product[ ] > ( [ ] ) ;
loading$ = new BehaviorSubject< boolean > ( false ) ;
@ ViewChild ( CdkVirtualScrollViewport) viewport! : CdkVirtualScrollViewport;
constructor (
private productService: ProductService,
private cdr: ChangeDetectorRef
) { }
@ debounce ( 300 )
onSearch ( term: string ) : void {
this . loading$. next ( true ) ;
this . productService. search ( term)
. pipe (
finalize ( ( ) => this . loading$. next ( false ) )
)
. subscribe ( products => {
this . products$. next ( products) ;
this . cdr. markForCheck ( ) ;
} ) ;
}
trackByProductId ( index: number , product: Product) : number {
return product. id;
}
}
@ Injectable ( {
providedIn: 'root'
} )
export class ProductService {
private readonly cache = new Map< string , Observable< any >> ( ) ;
constructor ( private http: HttpClient) { }
getProducts ( categoryId? : number ) : Observable< Product[ ] > {
const cacheKey = ` products_ ${ categoryId || 'all' } ` ;
if ( ! this . cache. has ( cacheKey) ) {
const request$ = this . http. get < Product[ ] > ( ` /api/products ` , {
params: categoryId ? { categoryId: categoryId. toString ( ) } : { }
} ) . pipe (
shareReplay ( 1 ) ,
delay ( 300000 ) ,
tap ( ( ) => this . cache. delete ( cacheKey) )
) ;
this . cache. set ( cacheKey, request$) ;
}
return this . cache. get ( cacheKey) ! ;
}
getProductsPaged ( page: number , size: number = 20 ) : Observable< PageResponse< Product>> {
return this . http. get < PageResponse< Product>> ( '/api/products' , {
params: { page: page. toString ( ) , size: size. toString ( ) }
} ) ;
}
}
监控与运维
1. 应用监控配置
management :
endpoints :
web :
exposure :
include : "*"
endpoint :
health :
show-details : always
metrics :
enabled : true
metrics :
export :
prometheus :
enabled : true
distribution :
percentiles-histogram :
http.server.requests : true
percentiles :
http.server.requests : 0.5 , 0.9 , 0.95 , 0.99
slo :
http.server.requests : 10ms, 50ms, 100ms, 200ms, 500ms
2. 自定义监控指标
@Component
public class BusinessMetrics {
private final Counter orderCounter;
private final Timer orderProcessingTimer;
private final Gauge activeUsersGauge;
public BusinessMetrics ( MeterRegistry meterRegistry) {
this . orderCounter = Counter . builder ( "orders.created" )
. description ( "Total orders created" )
. register ( meterRegistry) ;
this . orderProcessingTimer = Timer . builder ( "order.processing.time" )
. description ( "Order processing time" )
. register ( meterRegistry) ;
this . activeUsersGauge = Gauge . builder ( "users.active" )
. description ( "Currently active users" )
. register ( meterRegistry, this , BusinessMetrics :: getActiveUserCount ) ;
}
public void recordOrderCreated ( ) {
orderCounter. increment ( ) ;
}
public void recordOrderProcessingTime ( Duration duration) {
orderProcessingTimer. record ( duration) ;
}
private double getActiveUserCount ( ) {
return sessionService. getActiveUserCount ( ) ;
}
}
3. 日志聚合配置
< configuration>
< springProfile name = " prod" >
< appender name = " STDOUT" class = " ch.qos.logback.core.ConsoleAppender" >
< encoder class = " net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder" >
< providers>
< timestamp/>
< logLevel/>
< loggerName/>
< mdc/>
< message/>
< stackTrace/>
</ providers>
</ encoder>
</ appender>
< appender name = " FILE" class = " ch.qos.logback.core.rolling.RollingFileAppender" >
< file> /var/log/app/application.log</ file>
< rollingPolicy class = " ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy" >
< fileNamePattern> /var/log/app/application.%d{yyyy-MM-dd}.%i.log.gz</ fileNamePattern>
< maxFileSize> 100MB</ maxFileSize>
< maxHistory> 30</ maxHistory>
< totalSizeCap> 3GB</ totalSizeCap>
</ rollingPolicy>
< encoder class = " net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder" >
< providers>
< timestamp/>
< logLevel/>
< loggerName/>
< mdc/>
< message/>
< stackTrace/>
</ providers>
</ encoder>
</ appender>
< root level = " INFO" >
< appender-ref ref = " STDOUT" />
< appender-ref ref = " FILE" />
</ root>
</ springProfile>
</ configuration>
故障排除与调优
1. 常见问题诊断
echo "=== JVM 内存使用情况 ==="
jcmd < PID> GC.class_histogram | head -20
echo "=== 线程转储 ==="
jcmd < PID> Thread.print > thread_dump.txt
echo "=== 堆转储 ==="
jcmd < PID> GC.run_finalization
jcmd < PID> VM.gc
jmap -dump:format = b,file= heap_dump.hprof < PID>
echo "=== 数据库连接池状态 ==="
curl -s http://localhost:8080/management/metrics/hikaricp.connections | jq
echo "=== 缓存统计 ==="
curl -s http://localhost:8080/management/metrics/cache.gets | jq
2. 生产环境调优清单
production_checklist :
jvm_settings :
- name : "堆内存设置"
check : "-Xmx4g -Xms4g"
status : "✓"
- name : "GC算法选择"
check : "-XX:+UseG1GC"
status : "✓"
- name : "GC日志"
check : "-Xloggc:gc.log -XX:+PrintGCDetails"
status : "✓"
database_settings :
- name : "连接池大小"
check : "maximum-pool-size: 20"
status : "✓"
- name : "索引优化"
check : "主要查询字段已建立索引"
status : "✓"
- name : "慢查询监控"
check : "slow_query_log = 1"
status : "✓"
security_settings :
- name : "HTTPS配置"
check : "SSL证书有效"
status : "✓"
- name : "安全头设置"
check : "CSP, HSTS等已配置"
status : "✓"
- name : "敏感信息保护"
check : "密码等已加密存储"
status : "✓"