# micro-demo **Repository Path**: sayYi/micro-demo ## Basic Information - **Project Name**: micro-demo - **Description**: 微服务试验场 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-01-29 - **Last Updated**: 2022-06-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README - [x] 服务注册中心:nacos - [x] 配置中心:nacos - [x] 远程方法调用:dubbo - [x] 网关:gateway - [x] 日志、度量:loki,prometheus - [x] 分布式事务:seata # dubbo 添加依赖 ``` com.alibaba.cloud spring-cloud-starter-dubbo ``` 配置(使用spring cloud注册中心) ```yaml dubbo: scan: # dubbo 服务扫描基准包(服务实现类所在包) base-packages: com.sayyi.demo.micro.storage.api protocol: # dubbo 协议 name: dubbo # dubbo 协议端口( -1 表示自增端口,从 20880 开始) port: -1 registry: # 挂载到 Spring Cloud 注册中心 address: spring-cloud://localhost consumer: check: false ``` 接口声明 ```java public interface StorageApi { /** * 扣减商品库存 * @param updateStorageRequest * @return */ boolean updateStorage(UpdateStorageRequest updateStorageRequest); } ``` 实现方 ```java @DubboService(version = "1.0.0",interfaceClass = StorageApi.class) @Slf4j public class StorageApiImpl implements StorageApi { @Override public boolean updateStorage(UpdateStorageRequest updateStorageRequest) { log.info("invoked: [{}]", updateStorageRequest); return true; } } ``` 调用方 ```java @DubboReference(version = "1.0.0") private StorageApi storageApi; ``` # gateway ![image-20220215172105932](README.assets/image-20220215172105932.png) 1. gateway handler mapping决定一个请求应该走哪个路由 2. 路由上有一系列的filter,在调用前后执行 断言决定走哪个处理链 # seata seata整合spring: 业务场景预设:创建订单 1. 创建订单 2. 扣减库存:如果库存少于需求,则扣减失败 方便起见,只创建 订单、库存 两个服务 ## server 配置中心配置: 注册中心配置: 1. 下载seata文件 2. 修改`conf/registry.conf`文件,修改注册中心为nacos,使用文件作为配置中心(本地使用方便) 3. 修改`conf/file.conf`文件,使用db作为持久化组件 4. 启动服务`sh seata-server.sh -p 8091 -h 127.0.0.1` 使用nacos作为配置中心: 1. 修改conf/registry.conf`文件,使用nacos作为配置中心 2. 在 nacos 创建 `seataServer.properties`,参考config.txt文件,修改需要的配置 3. 启动服务 也可以使用官方提供的脚本上传nacos配置: ## 使用 ### AT模式 直接在方法上使用注解,启用全局事务 ``` @GlobalTransactional ``` ### TCC模式 需要在事务方法上添加注解,启动全局事务 ```java @GlobalTransactional ``` 在TCC接口上添加注解 > 对于远程接口,没有暴露try-commit/cancel的必要,内部实现是远程接口自己的事情 ```java package com.sayyi.demo.micro.order.service; import com.sayyi.demo.micro.order.entity.OrderTbl; import io.seata.rm.tcc.api.BusinessActionContext; import io.seata.rm.tcc.api.BusinessActionContextParameter; import io.seata.rm.tcc.api.LocalTCC; import io.seata.rm.tcc.api.TwoPhaseBusinessAction; @LocalTCC public interface OrderTccService { /** * * @param actionContext 调用方传递null即可 * @param orderTbl * @return */ @TwoPhaseBusinessAction(name = "OrderTccCreateOrder") int createOrder(BusinessActionContext actionContext, @BusinessActionContextParameter(paramName = "orderTbl") OrderTbl orderTbl); void commit(BusinessActionContext actionContext); void rollback(BusinessActionContext actionContext); } ``` ### saga模式 1. 数据库初始化: 2. client状态机配置 3. 节点服务类实现 4. 流程文件设计 5. 在全局事务中启动状态机 saga模式,也需要在全局事务中使用,否则无法触发提交行为。但是saga执行完毕后出现异常,并不能让saga回滚,所以最好在最后启动saga # 快速部署 集成maven插件 - assembly:构建、部署脚本打包 - exec:部署命令执行 快速打包部署: ``` mvn clean package exec:exec ``` # 日志系统 对接loki,使用grafana展示 logback loki appender: ``` com.github.loki4j loki-logback-appender-jdk8 1.3.2 org.apache.httpcomponents httpclient ``` xml ```xml ``` ```xml http://localhost:3100/loki/api/v1/push l=%level h=${HOSTNAME} c=%logger{20} t=%thread | %msg %ex true ``` # 度量 prometheus 依赖 ```xml org.springframework.boot spring-boot-starter-actuator io.micrometer micrometer-registry-prometheus 1.5.1 ``` 配置:暴露prometheus指标拉取接口 ```yaml management: endpoint: prometheus: enabled: true endpoints: web: exposure: include: prometheus ``` 自定义标签:application name ```java @Value("${spring.application.name}") private String application; @Bean MeterRegistryCustomizer meterRegistryCustomizer() { return registry -> registry.config().commonTags("application", application); } ``` 监控:prometheus server 配置 ```yaml scrape_configs: - job_name: 'spring' metrics_path: '/actuator/prometheus' static_configs: - targets: ['localhost:9700','localhost:9800','localhost:9900'] ``` prometheus - jvm gc,memory - 处理请求处理 - 正常请求数量 - 异常请求数量 - 请求平均响应时间 关于jmeter,想要实现的压测效果是 - 可以设置并发 - 可以设置压测时间:迭代一次,group内部使用无限循环 - 以及逐步提高并发数量有一个预热的过程 # Sentinel 在业务代码中,通过注解标注资源。资源的规则,使用sentinel dashboard来动态调整 ## sentinel dashboard 改造为nacos数据源 ## 网关 Xml ```xml com.alibaba.cloud spring-cloud-alibaba-sentinel-gateway com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.csp sentinel-datasource-nacos ``` 配置 ```yaml spring: cloud: sentinel: transport: dashboard: localhost:8080 port: 19000 # 使用nacos做持久化 datasource: # gateway api配置 gw-api-group: nacos: server-addr: localhost:8848 namespace: sentinel group-id: SENTINEL_GROUP data-id: ${spring.application.name}-gateway-api-rules data-type: json rule-type: gw-api-group # gateway流控配置 gw-flow: nacos: server-addr: localhost:8848 namespace: sentinel group-id: SENTINEL_GROUP data-id: ${spring.application.name}-gateway-flow-rules data-type: json rule-type: gw-flow # 熔断降级配置 degrade: nacos: server-addr: localhost:8848 namespace: sentinel group-id: SENTINEL_GROUP data-id: ${spring.application.name}-degrade-rules data-type: json rule-type: degrade # 系统规则 system: nacos: server-addr: localhost:8848 namespace: sentinel group-id: SENTINEL_GROUP data-id: ${spring.application.name}-system-rules data-type: json rule-type: system ``` ## 应用服务 依赖 ```xml com.alibaba.cloud spring-cloud-starter-alibaba-sentinel com.alibaba.csp sentinel-datasource-nacos ``` 配置 ```yaml spring: cloud: sentinel: transport: dashboard: localhost:8080 port: 19001 # 从nacos获取流控规则 datasource: # 微服务流控配置 flow: nacos: server-addr: localhost:8848 namespace: sentinel group-id: SENTINEL_GROUP data-id: ${spring.application.name}-flow-rules data-type: json rule-type: flow # 熔断降级配置 degrade: nacos: server-addr: localhost:8848 namespace: sentinel group-id: SENTINEL_GROUP data-id: ${spring.application.name}-degrade-rules data-type: json rule-type: degrade # 热点规则 param-flow: nacos: server-addr: localhost:8848 namespace: sentinel group-id: SENTINEL_GROUP data-id: ${spring.application.name}-param-flow-rules data-type: json rule-type: param-flow # 系统规则 system: nacos: server-addr: localhost:8848 namespace: sentinel group-id: SENTINEL_GROUP data-id: ${spring.application.name}-system-rules data-type: json rule-type: system # 授权规则 authority: nacos: server-addr: localhost:8848 namespace: sentinel group-id: SENTINEL_GROUP data-id: ${spring.application.name}-authority-rules data-type: json rule-type: authority ``` 资源标记 ``` @SentinelResource("OrderTblController:at") ``` # 附录 ## seata示例配置 ### registry.conf(nacos注册、配置) ``` registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "nacos" nacos { application = "seata-server" serverAddr = "127.0.0.1:8848" group = "SEATA_GROUP" namespace = "public" cluster = "default" username = "nacos" password = "nacos" } eureka { serviceUrl = "http://localhost:8761/eureka" application = "default" weight = "1" } redis { serverAddr = "localhost:6379" db = 0 password = "" cluster = "default" timeout = 0 } zk { cluster = "default" serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" } consul { cluster = "default" serverAddr = "127.0.0.1:8500" aclToken = "" } etcd3 { cluster = "default" serverAddr = "http://localhost:2379" } sofa { serverAddr = "127.0.0.1:9603" application = "default" region = "DEFAULT_ZONE" datacenter = "DefaultDataCenter" cluster = "default" group = "SEATA_GROUP" addressWaitTime = "3000" } file { name = "file.conf" } } config { # file、nacos 、apollo、zk、consul、etcd3 type = "nacos" nacos { serverAddr = "127.0.0.1:8848" namespace = "public" group = "SEATA_GROUP" username = "nacos" password = "nacos" dataId = "seataServer.properties" } consul { serverAddr = "127.0.0.1:8500" aclToken = "" } apollo { appId = "seata-server" ## apolloConfigService will cover apolloMeta apolloMeta = "http://192.168.1.204:8801" apolloConfigService = "http://192.168.1.204:8080" namespace = "application" apolloAccesskeySecret = "" cluster = "seata" } zk { serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" nodePath = "/seata/seata.properties" } etcd3 { serverAddr = "http://localhost:2379" } file { name = "file.conf" } } ``` ### seataServer.properties(nacos配置) ```properties #For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html #Transport configuration, for client and server transport.type=TCP transport.server=NIO transport.heartbeat=true transport.enableTmClientBatchSendRequest=false transport.enableRmClientBatchSendRequest=true transport.enableTcServerBatchSendResponse=false transport.rpcRmRequestTimeout=30000 transport.rpcTmRequestTimeout=30000 transport.rpcTcRequestTimeout=30000 transport.threadFactory.bossThreadPrefix=NettyBoss transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler transport.threadFactory.shareBossWorker=false transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector transport.threadFactory.clientSelectorThreadSize=1 transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread transport.threadFactory.bossThreadSize=1 transport.threadFactory.workerThreadSize=default transport.shutdown.wait=3 transport.serialization=seata transport.compressor=none #Log rule configuration, for client and server log.exceptionRate=100 #Transaction storage configuration, only for the server. The file, DB, and redis configuration values are optional. store.mode=DB store.lock.mode=DB store.session.mode=DB #Used for password encryption #store.publicKey= #These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block. store.db.datasource=druid store.db.dbType=mysql store.db.driverClassName=com.mysql.cj.jdbc.Driver store.db.url=jdbc:mysql://127.0.0.1:3306/seata?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true store.db.user=root store.db.password=root store.db.minConn=5 store.db.maxConn=30 store.db.globalTable=global_table store.db.branchTable=branch_table store.db.distributedLockTable=distributed_lock store.db.queryLimit=100 store.db.lockTable=lock_table store.db.maxWait=5000 #Transaction rule configuration, only for the server server.recovery.committingRetryPeriod=1000 server.recovery.asynCommittingRetryPeriod=1000 server.recovery.rollbackingRetryPeriod=1000 server.recovery.timeoutRetryPeriod=1000 server.maxCommitRetryTimeout=-1 server.maxRollbackRetryTimeout=-1 server.rollbackRetryTimeoutUnlockEnable=false server.distributedLockExpireTime=10000 server.session.branchAsyncQueueSize=5000 server.session.enableBranchAsyncRemove=true #Metrics configuration, only for the server metrics.enabled=false metrics.registryType=compact metrics.exporterList=prometheus metrics.exporterPrometheusPort=9898 ``` ## client-saga配置 状态机配置 ```java package com.sayyi.demo.micro.order.config; import io.seata.saga.engine.StateMachineEngine; import io.seata.saga.engine.config.DbStateMachineConfig; import io.seata.saga.engine.impl.ProcessCtrlStateMachineEngine; import io.seata.saga.rm.StateMachineEngineHolder; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; import javax.sql.DataSource; import java.io.IOException; import java.util.concurrent.ThreadPoolExecutor; @Configuration public class SagaStateMachineEngineConfig { @Bean public ThreadPoolExecutorFactoryBean threadExecutor() { ThreadPoolExecutorFactoryBean factoryBean = new ThreadPoolExecutorFactoryBean(); factoryBean.setThreadNamePrefix("SAGA_ASYNC_EXE"); factoryBean.setCorePoolSize(1); factoryBean.setMaxPoolSize(20); return factoryBean; } @Value("${seata.tx-service-group}") private String seataTxGroup; @Value("${spring.application.name}") private String applicationId; @Bean public DbStateMachineConfig dbStateMachineConfig(DataSource dataSource, ThreadPoolExecutorFactoryBean factoryBean) throws IOException { DbStateMachineConfig dbStateMachineConfig = new DbStateMachineConfig(); dbStateMachineConfig.setDataSource(dataSource); dbStateMachineConfig.setThreadPoolExecutor((ThreadPoolExecutor) factoryBean.getObject()); dbStateMachineConfig.setResources(new PathMatchingResourcePatternResolver() .getResources("classpath*:statelang/*.json")); dbStateMachineConfig.setEnableAsync(true); dbStateMachineConfig.setTxServiceGroup(seataTxGroup); dbStateMachineConfig.setApplicationId(applicationId); return dbStateMachineConfig; } @Bean public ProcessCtrlStateMachineEngine stateMachineEngine(DbStateMachineConfig dbStateMachineConfig) { ProcessCtrlStateMachineEngine engine = new ProcessCtrlStateMachineEngine(); engine.setStateMachineConfig(dbStateMachineConfig); return engine; } @Bean public StateMachineEngineHolder stateMachineEngineHolder(StateMachineEngine stateMachineEngine) { StateMachineEngineHolder engineHolder = new StateMachineEngineHolder(); engineHolder.setStateMachineEngine(stateMachineEngine); return engineHolder; } } ``` ## sentinel改造 v1.8.1 改造sentinel,使用nacos作为规则的持久化工具 本质上,就是将原本的,基于内存的操作,改为基于持久化组件的操作 1. 当前的sentinel策略存储方案 2. 涉及哪些模块需要改造 3. 如何改造 4. 客户端如何配合 ### 当前存储策略 全是基于内存存储,没有任何固化策略,重启东西就没了 ### 需要改造的模块 理论上来说,所有需哟新增的地方,都应该这么搞。通过页面请求,定位对应的代码 注册的应用,当前分为两种类型 1. 业务应用 2. 网关 #### 页面请求 业务应用: 1. 流控规则:http://localhost:8080//v1/flow/rules?app=sentinel-dashboard&ip=192.168.0.100&port=8719 2. 降级规则:http://localhost:8080/degrade/rules.json?app=sentinel-dashboard&ip=192.168.0.100&port=8719 3. 热点规则:http://localhost:8080//paramFlow/rules?app=sentinel-dashboard&ip=192.168.0.100&port=8719 4. 系统规则:http://localhost:8080/system/rules.json?app=sentinel-dashboard&ip=192.168.0.100&port=8719 5. 授权规则:http://localhost:8080//authority/rules?app=sentinel-dashboard&ip=192.168.0.100&port=8719 6. 集群流控(推送):http://localhost:8080//cluster/assign/single_server/sentinel-dashboard 网关: 1. API管理(专属):http://localhost:8080//gateway/api/list.json?app=micro-gateway&ip=192.168.0.100&port=19000 2. 流控规则(专属):http://localhost:8080//gateway/flow/list.json?app=micro-gateway&ip=192.168.0.100&port=19000 3. 降级规则:http://localhost:8080/degrade/rules.json?app=micro-gateway&ip=192.168.0.100&port=19000 4. 系统规则:http://localhost:8080/system/rules.json?app=micro-gateway&ip=192.168.0.100&port=19000 #### 代码块 业务: 1. 流控规则:FlowControllerV1.java 2. 降级规则:DegradeController 3. 热点规则:ParamFlowRuleController 4. 系统规则:SystemController 5. 授权规则:AuthorityRuleController 6. 集群流控:ClusterAssignController 网关: 1. 网关API管理:GatewayApiController 2. 网关流控规则:GatewayFlowRuleController ### 如何改造 客户端配置 ruleType类型:com.alibaba.cloud.sentinel.datasource.RuleType 找到对应的controller,将其中的 SentinelApiClient 替换为对应的 provider、publisher即可 ### 验证 以网关、sentinel dashboard自身进行测试 1. 通过控制台设置的流控规则是否有效 2. 控制台重启后,规则是否仍在