Spring Cloud Alibaba micro server tool set

Version: 2.2.1

1. Introduction

Spring Cloud Alibaba provides a one-stop solution for distributed application development. It contains all the components required to develop distributed applications, making it easy for you to develop your applications using Spring Cloud.

With Spring Cloud Alibaba, you only need to add some annotations and a small amount of configurations to connect Spring Cloud applications to the distributed solutions of Alibaba, and build a distributed application system with Alibaba middleware.

# 0. Original translation
- https://spring.io/projects/spring-cloud-alibaba
-Ali Cloud provides a one-stop solution for distributed application development. It contains all the components you need to develop distributed applications, making it easy for you to develop applications using springcloud.-With Ali Cloud, you only need to add some annotations and a little configuration to connect the Spring cloud application to Ali's distributed solution and build a distributed application system with Ali middleware.Copy the code

2. Environment construction

# 0. Build the project and introduce dependencies
Copy the code
<! -- Define springCloud version -->
<properties>
  <spring.cloud.alibaba.version>2.2.1. RELEASE</spring.cloud.alibaba.version>
</properties>

<! -- SpringCloudAlibaba download dependencies globally, no dependencies will be introduced
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-dependencies</artifactId>
      <version>${spring.cloud.alibaba.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
Copy the code

3.Nacos

What is Nacos Name Service & Configurations Services

- https://nacos.io/zh-cn/index.html
-Nacos is dedicated to helping you discover, configure, and manage microservices. Nacos provides an easy-to-use feature set that helps you quickly implement dynamic service discovery, service configuration, service metadata, and traffic management.Copy the code
  • Summary :Nacos is the service registry and unified configuration center in the microservices architecture, replacing the original (Eureka, Consul) and Config components

Install Nacos

# 0. Prepare the environment
-1.64-bit OS: Supports Linux, Unix, Mac, and Windows. Linux, Unix, and Mac are recommended.-2.64 bit JDK 1.8+; Download & Configure.-3. The Maven 3.2. X +; Download & Configure.# 1. Download nacOS [This course version:] [1.3.0 version]
- https://github.com/alibaba/nacos/releases 
Copy the code

# 2. Unzip the installation package to the specified location
-Bin Script directory for starting the nacos service-Conf nacos Configuration file directory-Target nacos Directory for storing boot dependencies-Data nacos Directory for saving data after successful startupCopy the code

# 3. Start the installation service
-Linux/Unix/MAC startedGo to the bin directory of nacos and run the following command./startup.sh -m standalone
-Windows startupRun the startup. CMD -m standalone file in CMD or double-click the startup.Copy the code

# 4. Access the Web services management interface of NacOS
- http://localhost:8848/nacos/
-The username and password are both nacosCopy the code

Develop services registered with nacOS

# 0. Create the project and import dependencies
Copy the code
<! -- Introducing nacOS client dependencies -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
Copy the code

# 1. Configure the registration address
Copy the code
server.port=8789 # Specify current service port
spring.application.name=Nacosclient # Specify the service name
spring.cloud.nacos.server-addr=Localhost :8848 # specifies the nacOS service address
spring.cloud.nacos.discovery.server-addr=${spring. Cloud. Nacos. Server - addr} # designated registry address
management.endpoints.web.exposure.include=* # Expose all Web endpoints
Copy the code
# 2. Add startup service registration notes [Note:] [This step can be omitted after the new version]
Copy the code

# 3. View a list of nacOS services
Copy the code

Use NACOS as the configuration center

1. Obtain the configuration from nacOS

# 1. Create a project and introduce nacons to configure central dependencies
Copy the code
<! -- Introducing nacOS Client dependencies -->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<! -- Add nacOS config dependency -->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
Copy the code

# 2. Configure the configuration center address
Copy the code
spring.cloud.nacos.server-addr=Localhost :8848 # Address of the remote configuration center
spring.cloud.nacos.config.server-addr=${spring. Cloud. Nacos. Server - addr} # remote configuration center address
spring.cloud.nacos.config.group=DEFAULT_GROUP # Reads configured groups
spring.cloud.nacos.config.file-extension=Properties # specifies the read file suffix
spring.application.name=Config # specifies the prefix to read the file
spring.profiles.active=Prod # specifies the environment in which the file is read
Copy the code

# 3. Create configuration in NACOS
Copy the code

# 4. Write controller test configuration reads
Copy the code
@RestController
@Slf4j
public class HelloController {
    // Inject the configuration
    @Value("${user.name}")
    private String username;
    @GetMapping("/hello/config")
    public String config(a){
        log.info("Username: [{}]",username);
        returnusername; }}Copy the code

# 5. Start project mode test configuration read
Copy the code

2. DataId

# 1.DataId
-The complete format of the specific configuration file used to read the remote configuration center is as follows:- ${prefix}-${spring.profile.active}.${file-extension}
A. the prefix default for spring. Application. The value of the name, but can be by spring configuration items. Cloud. Nacos. Config. The prefix to configuration. B. Spring.profile. active indicates the profile corresponding to the current environment. For details, see the Spring Boot document. Note: ${prefix}.${file-extension} c. File -exetension fixed tension of the dataId: ${prefix}.${file-extension} Item can be configured spring. Cloud. Nacos. Config. The file - the extension to the configuration. Currently, only properties and YAML types are supported.Copy the code

3. Refresh the configuration automatically

# 1. Automatic refresh
-Nacos already provides automatic configuration refreshes by default. If you need to refresh the configuration, add the @refreshScope annotation to the controllerCopy the code
@RestController
@Slf4j
@RefreshScope
public class HelloController {
    // Inject the configuration
    @Value("${user.name}")
    private String username;
    @GetMapping("/hello/config")
    public String config(a){
        log.info("Username: [{}]",username);
        returnusername; }}Copy the code

4. Namespace

# 1. Namespace
- https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config
-The namespace is designed for nacOS enterprise development to distinguish between different environments, such as test environment, production environment, and other environments. Therefore, in order to ensure the isolation of different environment configurations, the concept of namespace is proposed. By default, there is a public in NACOS Namespace All configurations are obtained in this namespace if no namespace is specified. In actual development, different namespace Spaces can be created for different environments. Default space cannot be deleted!Copy the code

# 2. Create additional namespaces
-Each namespace has a unique ID that uniquely identifies the specified space when the configuration is readCopy the code

# 3. View space in the configuration list
Copy the code

# 4. Download the create configuration file in the specified space
Copy the code

# 5. Use namespaces to specify configurations in projects
Copy the code

# 6. Test the configuration
Copy the code

5. Configure groups

# 1. Configure groups.
-A configuration group is a grouping of configuration sets, which is represented by a meaningful string (such as Buy or Trade). Different configuration groups can have the same configuration set (Data ID). When you create a configuration on Nacos, if the name of the configuration group is not specified, the name of the configuration group defaults to DEFAULT_GROUP. Common scenarios for configuring groups are as follows: Groups can be used to distinguish different projects or applications. For example, in the student management system configuration set, a group can be STUDENT_GROUP.Copy the code

# 2. Create a group
Copy the code

# 3. Read configurations for different groupings
Copy the code

4. Sentinel traffic Guard

What is a sentinel

As microservices become popular, the stability of service calls is becoming increasingly important. Sentineltakes “flow” as the breakthrough point, and works on multiple fields including flow control, Circuit breaking and Load protection to protect service reliability.

# 0. Specifications
- https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/en-us/index.html#_how_to_use_sentinel
- https://github.com/alibaba/Sentinel/wiki
-With the popularity of microservices, the stability of service invocation becomes more and more important. Sentinel takes "flow" as the breakthrough point and works in many fields such as flow control, circuit breaking and load protection to ensure service reliability.-Popular: Used to protect the role of microservice pairs in microservice systems how to service avalanche service fuse service downgrading is used to replace Hystrix# 1. Features
-Rich application scenarios: Sentinel has undertaken the core scenarios of Alibaba's double Eleven traffic drive in the past 10 years, such as SEC killing (i.e. burst traffic control within the range of system capacity), message peaking and valley filling, cluster flow control, real-time fusing of unavailable downstream applications, etc.-Complete real-time monitoring: Sentinel also provides real-time monitoring capabilities. From the console, you can see a summary of the performance of a single machine-by-second data, or even a cluster of less than 500 machines, for accessing the application.-Extensive Open source ecosystem: Sentinel provides out-of-the-box integration modules with other open source frameworks/libraries, such as Spring Cloud, Dubbo, and gRPC. You can quickly access Sentinel by introducing the appropriate dependencies and simple configuration.Copy the code

Sentinel use

-Sentinel provides two service components:One is sentinel, which is used to realize service fusing and degradation in microservice system. This is similar to Hystrix and the Sentinel Dashboard is used to monitor traffic calls and things like that in microservices. This is similar to HystrixCopy the code

1. Sentinel Dashboard installation

# 1. Download
- https://github.com/alibaba/Sentinel/releases
Copy the code

# 2. The start
-The dashboard is a JAR package that can be started directly with a Java command such as java-jar. The default port is 8080-Java - Dserver. Port = 9191 - jar sentinel - dashboard - 1.7.2. JarCopy the code

# 3. Access the Web interface
- http://localhost:9191/#/login
Copy the code

# 4. To log in
-Username & Password: sentinelCopy the code

2. Sentinel real-time monitoring service

# 1. Create a project to introduce dependencies
Copy the code
<! -- Introducing nacOS Client dependencies -->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<! -- Introducing sentinel dependencies -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
Copy the code

# 2. Configuration
Copy the code
server.port=8789
spring.application.name=nacosclient
spring.cloud.nacos.server-addr=localhost:8848
spring.cloud.nacos.discovery.server-addr=${spring.cloud.nacos.server-addr}

spring.cloud.sentinel.enabled=True # Enable Sentinel is enabled by default
spring.cloud.sentinel.transport.dashboard=Localhost :9191 # Connect dashboard
spring.cloud.sentinel.transport.port=8719 # Port to communicate with Dashboard
Copy the code

# 3. Start the service
Copy the code

# 4. Access the Dashboard interface to view service monitoring
-Found nothing in the interface?-By default sentiel is lazy-loaded and does not create service monitoring immediately after startup, but only when the service needs to be calledCopy the code

# 5. Develop services
Copy the code
@RestController
@Slf4j
public class SentinelController {
    @GetMapping("/sentinel/test")
    public String test(a){
        log.info("sentinel test");
        return "sentinel test ";
    }

    @GetMapping("/sentinel/test1")
    public String test1(a){
        log.info("sentinel test1");
        return "sentinel test1 "; }}Copy the code

# 6. Initiate the call
- http://localhost:8789/sentinel/test
Copy the code

# 7. View the monitoring interface
Copy the code

3. Sentinel flow control

# 0. Specifications
-Flow control monitors application traffic indicators, such as QPS or concurrent threads, and controls the traffic when it reaches a specified threshold to avoid being overwhelmed by instantaneous traffic peaks and ensure high availability of applications.-Multiple traffic limiting rules can be created for a resource. FlowSlot traverses all the limited flow rules of the resource until a rule triggers flow limiting or all the rules are traversed.-A traffic limiting rule mainly consists of the following factors, which can be combined to achieve different traffic limiting effects:Resource: resource name, which is the object of the traffic limiting rule Count: traffic limiting threshold Grade: type of traffic limiting threshold (QPS or number of concurrent threads) limitApp: call source for traffic control. If the value is default, the call source strategy is not differentiated. ControlBehavior: Flow control effect (direct reject, Warm Up, uniform queuing)
-There are two main statistical types of traffic control, one is counting the number of concurrent threads, the other is counting QPS-More details see the website: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6Copy the code
QPS current-limiting
# 1. Configure QPS flow control
Copy the code

# 2. Tests
-You can receive a maximum of one request per second. If more than one request is received, an error is reportedCopy the code

Number of threads flow limiting
# 1. Configure thread flow limiting
Copy the code

# 2. Access tests
Copy the code

Flow control mode
# 1
-Direct: Indicates that traffic control is triggered when the traffic control rule reaches the threshold-Association: Two resources are associated when there is a resource contention or dependency relationship between them. For example, the read and write operations of the same database field compete. If the read speed is too high, the write speed will be affected, and the write speed will be affected. If read and write operations are left to scramble for resources, the cost of scrambling itself can reduce overall throughput. Association limiting can be used to avoid excessive contention between related resources, for example, read_db and write_The two resources, DB, represent database reads and writes, so we can give read_db Sets traffic limiting rules to achieve write priority: Set strategy to ruleconstant.strategy_RELATE also sets the refResource to write_db. In this way, when the write library operation is too frequent, the data read requests will be limited.Copy the code

-Current-limiting link: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6Copy the code
Effect of flow control
-Direct rejection :(ruleconstant.control_BEHAVIOR_DEFAULT) is the DEFAULT flow control mode. When QPS exceeds the threshold of any rule, new requests will be immediately rejected by throwing FlowException.-Warm Up: (RuleConstant. CONTROL_BEHAVIOR_WARM_UP), that is, warm/cold startup mode. When the system has been at a low water level for a long time, when the flow suddenly increases, directly pulling the system to a high water level can suddenly overwhelm the system. Through the "cold start", the flow slowly increases to the upper limit of the threshold in a certain period of time, giving the cold system a time to warm up, preventing the cold system from being overwhelmed. More: https://github.com/alibaba/Sentinel/wiki/%E9%99%90%E6%B5%81---%E5%86%B7%E5%90%AF%E5%8A%A8 - Uniform queuing :(ruleconstant.control_BEHAVIOR_RATE_LIMITER will strictly control the interval between requests, that is, let the requests pass at an even speed, corresponding to the leak-bucket algorithm. Requests can only be queuedMore: https://github.com/alibaba/Sentinel/wiki/%E6%B5%81%E9%87%8F%E6%8E%A7%E5%88%B6-%E5%8C%80%E9%80%9F%E6%8E%92%E9%98%9F%E6 %A8%A1%E5%BC%8FCopy the code

4. Fuse downgrade

# 0. Specifications
- https://github.com/alibaba/Sentinel/wiki/%E7%86%94%E6%96%AD%E9%99%8D%E7%BA%A7
-In addition to flow control, fusing downgrading of unstable resources in call links is also one of the important measures to ensure high availability. Because of the complexity of the invocation relationship, if a resource in the invocation link is unstable, requests will eventually pile up. Sentinel** Fuse downgrade **When a resource in the invocation link is in an unstable state (for example, the invocation times out or the proportion of exceptions increases), the invocation of this resource is restricted to make the request fail quickly and avoid cascading errors caused by affecting other resources. When a resource is degraded, all calls to the resource are automatically fuses within the next downgrade window (the default behavior is thrown)`DegradeException`).Copy the code
Demotion strategy
  • Average response time (DEGRADE_GRADE_RT) : If N requests arrive within 1s and the response time exceeds the threshold (count), the survival time of the three groups exceeds the threshold in seconds. Calls to this method will automatically fuse (throw a DegradeException). Pay attention to the Sentinel default statistical RT limit is 4900 ms, is beyond the threshold will be classified as 4900 ms, if need to change this limit by startup configuration items – Dcsp. Sentinel. Statistic. Max. RT = XXX to configure.

  • Abnormal ratio (DEGRADE_GRADE_EXCEPTION_RATIO) : The total number of requests per second is greater than or equal to N, and the ratio of the total number of exceptions per second to the total number of passes exceeds the threshold for survival. The survival rate is reduced to the next timeWindow. Calls to this method are automatically returned. The threshold range for the abnormal ratio is [0.0, 1.0], representing 0-100%.

  • Number of exceptions (DEGRADE_GRADE_EXCEPTION_COUNT) : When the number of exceptions in the last 1 minute exceeds the threshold, a resource is fused. Notice the statistical timeWindow is minute. If the timeWindow is less than 60 seconds, the circuit breaker may enter the circuit breaker state again after the circuit breaker state ends.

5. SentinelResource annotation

# 0. Specifications
- https://github.com/alibaba/Sentinel/wiki/%E6%B3%A8%E8%A7%A3%E6%94%AF%E6%8C%81
Copy the code

 @GetMapping("/sentinel/test1")
    @SentinelResource(value = "aa",blockHandler = "fallBack",fallback = "fall")
    public String test1(int id){
        log.info("sentinel test1");
        if(id<0)		
            throw new RuntimeException("Invalid parameter!!");
        }
        return "sentinel test1 :"+id;
    }
		// Degrade exception handling
    public String fallBack(int id,BlockException e){
            if(e instanceof FlowException){
                return "Current service is flow controlled! "+e.getClass().getCanonicalName();
            }
            return "The current service has been degraded! "+e.getClass().getCanonicalName();
    }
		// Exception handling
    public String fall(int id){
        return "Current service is unavailable!";
    }
Copy the code

5. Integrate environmental common dependencies

Spring 2.2 + boot

springcloud Hoxton

Springcloud alibaba 2.2.1 +

# 0. Build the project and introduce dependencies
Copy the code

<properties>
  <java.version>1.8</java.version>
  <spring-cloud.version>Hoxton.SR6</spring-cloud.version>
  <spring.cloud.alibaba.version>2.2.1. RELEASE</spring.cloud.alibaba.version>
</properties>

<dependencyManagement>
  <dependencies>
    <! -- Introducing SpringCloud Alibaba -->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-dependencies</artifactId>
      <version>${spring.cloud.alibaba.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
    <! - introduce springcloud -- -- >
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-dependencies</artifactId>
      <version>${spring-cloud.version}</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
Copy the code

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — I’m line — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

Nacos

IO/zh-CN /docs/…

1. Nacos service registration and discovery

1.1 start the Nacos service

IO /zh-cn/docs/…

Then go to nacos->bin->startup. CMD and double-click startup. CMD.

The following page is displayed when you access localhost:8848

1.2 Creating a Maven parent project

pom.xml

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
Copy the code

1.3 Service Providers

@SpringBootApplication
@EnableDiscoveryClient // The new version can be added or not
public class NacosProviderApplication {

    public static void main(String[] args) {
        SpringApplication.run(NacosProviderApplication.class, args);
    }

    @RestController
    class EchoController {
        @RequestMapping(value = "/echo/{string}", method = RequestMethod.GET)
        public String echo(@PathVariable String string) {
            return "Hello Nacos Discovery "+ string; }}}Copy the code

application.yml

spring:
  cloud:
    nacos:
      discovery:
        server-addr: 127.0. 01.: 8848 # # # # # # # # # #
  application:
    name: service-provider  Note that the name should not contain _ or special characters
server:
  port: 8090
Copy the code

1.4 consumers

@SpringBootApplication
@EnableDiscoveryClient
public class NacosConsumerApplication {

    public static void main(String[] args) {
        SpringApplication.run(NacosConsumerApplication.class, args);
    }

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate(a) {
        return newRestTemplate(); }}@RestController
public class TestController {
    @Autowired
    private  RestTemplate restTemplate;

    @RequestMapping(value = "/echo/{str}", method = RequestMethod.GET)
    public String echo(@PathVariable String str) {

        return restTemplate.getForObject("http://service-provider/echo/"+ str, String.class); }}Copy the code

2. Nacos domain model division

NameSpace: NameSpace. The default NameSpace is public. For example, if we develop and test environments with the same NACOS, we must have different interface addresses, and it is not recommended to configure the test environment randomly during development. In this case, we should use namespace to isolate our space. group: grouping. For example, add our user service, order service, warehousing service and logistics service. The order service has an interface called getData, and the warehousing service also has an interface called getData. Our user service only wants to call getData of our order service. If you don’t want to call getData for the warehouse service, you can isolate it with a group.

cluster: the cluster. For example, we now have two clusters, one is the order service cluster in Beijing, the commodity service cluster in Beijing, and another is the order service cluster in Nanjing, the commodity service cluster in Nanjing. We hope that the order cluster in Beijing will call the commodity system in Beijing first, and the cluster service in Nanjing will call the cluster service in Nanjing first. You don’t want us to call remotely across regions (you can call if there are no services in the group, but those in the same cluster are preferred)

metadata: Only for version control. For example, we may have multiple versions coexist in development. The V1 version of the order can only be adjusted to the V1 version of the product, and the V2 version of the order can only be adjusted to the V2 version of the product.

Configuration:

spring:
  application:
    name: test-demo
  cloud:
    nacos:
      discovery:
        server-addr: 127.0. 01.: 8848
        namespace: 0610f97e-c25d-4f49-bfb8-b340e3584b82
        group: test
        cluster-name: BJ-cluster
        metadata:
          current-version: V1
server:
  port: 8888
Copy the code

Ribbon

Is an open source client load balancer. The Ribbon gets a list of service addresses to invoke from Nacos, uses its own algorithm to compute an instance, and sends it to restTemplate to request. There is no need to add dependencies because the Nacos-Discovery package already includes the ribbon concept in the previous blog, which explains the configuration here.

// Annotations need to be written on restTemplate, integrate ribbon for restTemplate
@Bean
@LoadBalanced
public RestTemplate restTemplate(a){
    return new RestTemplate();
}
Copy the code

When the restTemplate request is made, the Ribbon automatically converts the HTTP address of the SERVER’s URL in NacOS to the server’s own service name. For example, http://localhost:8080/users/ will to http://user-center/users/ {id} {id}

The composition of the ribbon

In Spring Cloud, the Ribbon default configuration is as follows:

IClientConfig: Ribbon client configuration, the default with com.net flix. Client. Config. DefaultClientConfigImpl implementation. IRule: Ribbon load balancing strategy, default with com.net flix. Loadbalancer. ZoneAvoidanceRule implementation, the strategy can in many instances of regional environment choose the best area in access IPing: Ribbon instance inspection strategy, default with com.net flix. Loadbalancer. NoOpPing implementation, the strategy is a special examination, in fact, he does not check whether the instance is available, but always returntrue, think all service instance can be used by default ServerList < Server > : service instance list maintenance mechanism, the default with com.net flix. The loadbalancer. ConfigurationBasedServerList implementation. ServerListFilter < Server > : Service instance list filtering mechanism, the default use org.springframework.cloud.net flix. Ribbon. ZonePreferenceServerListFilter implementation, This strategy can filter out priority and request calls to handle with regional service implementation ILoadBalancer: load balancer, default with com.net flix. Loadbalancer. ZoneAwareLoadBalancer realization, he has the capability of the area of perceptionCopy the code

IRule Configuration (Load Balancing rules in the Ribbon)

Java code configuration implementation

// Note that this class should be placed outside of the startup class
@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbonRule(a){
        return newRandomRule(); }}/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
/ * * * UserCenterRibbonConfiguration for custom implementation User - center service ribbon client * *@author< a href = "mailto:[email protected]" > Isaac. Zhang | if early < / a > *@since2019/7/13 * /
@Configuration
@RibbonClient(name = "user-center", configuration = RibbonConfiguration.class)
public class UserCenterRibbonConfiguration {}/ * -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - * /
@Configuration
/ / @ RibbonClient (name = "user - center", the configuration = RibbonConfiguration. Class) / / scope for user - center
@RibbonClients(defaultConfiguration = RibbonConfiguration.class) // Scope is global
public class UserCenterRibbonConfiguration {}Copy the code

Using configuration files

user-center: # service name
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule The full path name of the rule class
Copy the code

Best practices

  • Use property configuration whenever possible
  • Keep a single configuration in the same micro-service. Do not mix the configurations, which increases the complexity of location

Ribbon Other configuration options

For example, modify IPing rules

Code way
@Configuration
public class RibbonConfiguration {
    @Bean
    public IPing ping(a){
        return newPingUrl(); }}Copy the code
The configuration file

Ribbon Hunger loading

By default, the Ribbon is lazy and creates the client on the first request.

ribbon:
  eager-load:
    enabled: true # Hunger load active
    clients: user-center,xxx,xxx Which clients are enabled
Copy the code

Extended Ribbon – Supports Nacos weights

  • interfacecom.netflix.loadbalancer.IRule
  • An abstract classcom.netflix.loadbalancer.AbstractLoadBalancerRule

Either way is fine.

@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class NacosWeightRule extends AbstractLoadBalancerRule {

    private final NacosDiscoveryProperties nacosDiscoveryProperties;

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        // Read the configuration file and initialize NacosWeightRule4Ribbon
    }

    @Override
    public Server choose(Object key) {

        try {
            // ILoadBalancer is the entrance to the Ribbon. Basically all the elements we want can be found in this object
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            log.info("NacosWeightRule4Ribbon lb = {}", loadBalancer);
            // The name of the microservice you want to request
            String name = loadBalancer.getName();

            // Implement load balancing algorithm
            // API for service discovery (nacOS internal implementation)
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();

            // Nacos Client selects an instance using a weight-based load balancing algorithm (built-in)
            Instance instance = namingService.selectOneHealthyInstance(name);
            log.info("port = {}, weight = {}, instance = {}", instance.getPort(), instance.getWeight(), instance);
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("NacosWeightRule4Ribbon {}", e.getMessage());
        }
        return null; }}@Configuration
public class RibbonConfiguration {
    @Bean
    public IRule ribbionRule(a){
        return newNacosWeightRule(); }}@Configuration
@RibbonClients(defaultConfiguration = RibbonConfiguration.class) // Global configuration
public class UserCenterRibbonConfiguration {}Copy the code

The preceding configuration mode is global configuration or configuration file configuration. For details, see the preceding. Change the weight size in the NACOS console by setting the weight number for the instance.

user-center: # service name
  ribbon:
    NFLoadBalancerRuleClassName: com.**.**.NacosWeightRule  
Copy the code

The Ribbon prioritizes load balancing rules for the same cluster

spring:
  cloud:
    nacos: 
      discovery:
        Set a cluster name, such as HAOZI
        cluster-name: HAOZI
Copy the code
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
 
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {}@Override
    public Server choose(Object key) {
        try {
            // Get the cluster name HAOZI in the configuration file
            String clusterName = nacosDiscoveryProperties.getClusterName();
            //loadBalancer is the entrance to the ribbon
            BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
            // The name of the microservice you want to request
            String name = loadBalancer.getName();
            // Get the service discovery API
            NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
            //1. Find all instances A of the specified service
            //true means to take only healthy instances
            List<Instance> instances = namingService.selectInstances(name , true);
            //2. Filter out all instance B in the same cluster
            List<Instance> sameClusterInstances = instances.stream()
                .filter(instance -> Objects.equals(instance.getClusterName(), clusterName))
                .collect(Collectors.toList());
            //3. If B is empty, use A
            List<Instance> instancesToBeChosen = new ArrayList<>();
            if(CollectionUtils.isEmpty(sameClusterInstances)){
                instancesToBeChosen = instances;
            }else{
                instancesToBeChosen = sameClusterInstances;
            }
            //4. Load balancing algorithm based on weights, return an example
            Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChosen);
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("Abnormal",e);
            return null; }}}// There is no load-balancing method called based on weights, so find it in the source code, but getHostByRandomWeight is protected,
// So the write class inherits the class and calls the method by subclass and returns
class ExtendBalancer extends Balancer {
    public static Instance getHostByRandomWeight2(List<Instance> hosts){
        returngetHostByRandomWeight(hosts); }}Copy the code

Ribbon load balancing rules based on metadata-based version control

Write the configuration

spring:
  cloud:
    nacos:
        metadata: 
          # own version of this instance
          version: v1
          The version of the provider that is allowed to be invoked
          target-version: v1
Copy the code

Write the rules

public class NacosFinalRule extends AbstractLoadBalancerRule {
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;
 
    @Override
    public Server choose(Object key) {
        // Load balancing rule: Select instances that meet metadata requirements in the same cluster
        // If not, select all instances that match metadata in the cluster
        try {
            // Get the cluster name from the configuration file
            String clusterName = this.nacosDiscoveryProperties.getClusterName();
            // Use metadata to get the version information that can be called
            String targetVersion = this.nacosDiscoveryProperties.getMetadata().get("target-version");
 
            DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
            // The name of the microservice
            String name = loadBalancer.getName();
            // Get the service API
            NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance();
 
            // 1. Query all instance A
            List<Instance> instances = namingService.selectInstances(name, true);
 
            List<Instance> metadataMatchInstances = instances;
            // 2. Filter instance B that matches metadata
            // If version mapping is configured, only instances of metadata matching are called
            if (StringUtils.isNotBlank(targetVersion)) {
                metadataMatchInstances = instances.stream()
                        .filter(instance -> Objects.equals(targetVersion, instance.getMetadata().get("version")))
                        .collect(Collectors.toList());
                if (CollectionUtils.isEmpty(metadataMatchInstances)) {
                    log.warn("No metadata matching target instance found! Check the configuration. targetVersion = {}, instance = {}", targetVersion, instances);
                    return null;
                }
            }
 
            List<Instance> clusterMetadataMatchInstances = metadataMatchInstances;
            // 3. Select instance C that matches the metadata in cluster
            // If the cluster name is specified, filter the instances whose metadata matches the cluster
            if(StringUtils.isNotBlank(clusterName)) { clusterMetadataMatchInstances = metadataMatchInstances.stream() .filter(instance  -> Objects.equals(clusterName, instance.getClusterName())) .collect(Collectors.toList());// 4. If C is empty, use B
                if (CollectionUtils.isEmpty(clusterMetadataMatchInstances)) {
                    clusterMetadataMatchInstances = metadataMatchInstances;
                    log.warn("A cross-cluster call occurred. clusterName = {}, targetVersion = {}, clusterMetadataMatchInstances = {}", clusterName, targetVersion, clusterMetadataMatchInstances); }}// 5. Randomly select instances
            Instance instance = ExtendBalancer.getHostByRandomWeight2(clusterMetadataMatchInstances);
            return new NacosServer(instance);
        } catch (Exception e) {
            log.warn("Abnormal", e);
            return null; }}@Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {}}public class ExtendBalancer extends Balancer {
    /** * select instance ** randomly according to weight@paramInstances List of instances *@returnThe selected instance */
    public static Instance getHostByRandomWeight2(List<Instance> instances) {
        returngetHostByRandomWeight(instances); }}Copy the code

Note: The start category forgot to inject the RestTemplate

@Bean
@LoadBalanced
public RestTemplate restTemplate(a) {
    RestTemplate template = new RestTemplate();
    return template;
}
Copy the code

Feign

The composition of Feign

Feign’s diary level

How to integrate Feign in the first place

Follow SpringBoot’s three axe

Step 1: Add dependencies

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Copy the code

Step 2: Write notes

@EnableFeignClients // Add to the startup class
Copy the code

Step 3: Write configuration, no configuration required

How do I add a log level to Feign

Method 1: Code implementation step 1: Add Feign Configuration class, can be added under the main class, but do not add @Configuration. If @Configuration is added and placed under the main class, then all Feign client instances are shared, just like the Ribbon Configuration class. If you must add @Configuration, put it in a package outside the main class load. It is recommended not to add @Configuration.

public class FeignConfig {
    @Bean
    public Logger.Level Logger(a) {
        returnLogger.Level.FULL; }}Copy the code

Step 2: Add a configuration class to @FeignClient

@FeignClient(name = "goods", configuration = FeignConfig.class)
Copy the code

Step 3: Write the configuration

logging:
  level:
    com.xxx.xxx.FeignAPI: DEBUG # Enable logging format to logging.level.+Feign client path
Copy the code

Method 2: Configure attribute implementation

feign:
  client:
    config:
      The name of the microservice you want to invoke
      server-1:
        loggerLevel: FULL
Copy the code

If the Ribbon Configuration class is used, all Feign client instances will be shared. This is the same as the Ribbon Configuration class. The only correct way to have a parent-child context ComponentScan overlap (strongly not recommended)

// Add the defaultConfiguration configuration for the @enableFeignClients annotation on the startup class
@EnableFeignClients(defaultConfiguration = FeignConfig.class)
Copy the code

Method 2: Configure attribute implementation

feign:
  client:
    config:
      Change the name of the microservice to default and set it to global
      default:
        loggerLevel: FULL
Copy the code

Ribbon configuration VS Feign configuration

Feign code vs. configure properties

How to construct a multi-parameter request using Feign

GET Specifies the URL for requesting multiple parameters

Suppose the URL to be requested contains multiple parameters, such as http://microservice-provider-user/get? Id =1&username= 1;

We know that Spring Cloud adds Spring MVC annotation support to Feign, so let’s try it out with Spring MVC:

@FeignClient("microservice-provider-user")
public interface UserFeignClient {
  @RequestMapping(value = "/get", method = RequestMethod.GET)
  public User get0(User user);
}
Copy the code

However, this is not written correctly, and the console prints an exception similar to the following.

feign.FeignException: status 405 reading UserFeignClient#get0(User); content:
{"timestamp":1482676142940,"status":405,"error":"Method Not Allowed","exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/get"}
Copy the code

As you can see from the exception, Feign uses the POST method to send the request even though we specified the GET method. And that leads to an anomaly. The correct way to write it is as follows

Method 1 [Recommendation]

@FeignClient("microservice-provider-user")
public interface UserFeignClient {
  @GetMapping("/get")
  public User get0(@SpringQueryMap User user);
}
Copy the code

Method 2 [Recommendation]

@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
  @RequestMapping(value = "/get", method = RequestMethod.GET)
  public User get1(@RequestParam("id") Long id, @RequestParam("username") String username);
}
Copy the code

This is the most intuitive way, urls have several parameters, and methods in the Feign interface have several parameters. Use the @requestParam annotation to specify what the parameters of the request are.

The POST request contains multiple parameters

Let’s discuss how to use Feign to construct a POST request with multiple parameters. Suppose the service provider’s Controller is written like this:

@RestController
public class UserController {
  @PostMapping("/post")
  public User post(@RequestBody User user) {... }}Copy the code

How do we use Feign to request? The answer is very simple, for example:

@FeignClient(name = "microservice-provider-user")
public interface UserFeignClient {
  @RequestMapping(value = "/post", method = RequestMethod.POST)
  public User post(@RequestBody User user);
}
Copy the code

Sentinel

Parameter summary of Alibaba Sentinel rule

Written based on Sentinel 1.6.2.

I. Flow control rules

1.1 configuration 1.2 parameter 1.3 Code Configuration examples

private void initFlowQpsRule(a) {
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule(resourceName);
    // set limit qps to 20
    rule.setCount(20);
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    rule.setLimitApp("default");
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}
Copy the code

1.4 Reference: github.com/alibaba/Sen… 1.5 reference github.com/alibaba/Sen…

Second, demotion rules

2.1 configuration 2.2 parameter 2.3 Code Configuration Examples

private void initDegradeRule(a) {
    List<DegradeRule> rules = new ArrayList<>();
    DegradeRule rule = new DegradeRule();
    rule.setResource(KEY);
    // set threshold RT, 10 ms
    rule.setCount(10);
    rule.setGrade(RuleConstant.DEGRADE_GRADE_RT);
    rule.setTimeWindow(10);
    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}
Copy the code

2.4 Reference: github.com/alibaba/Sen…

Hotspot rules

3.1 configuration 3.2 parameter 3.3 Code Configuration examples

ParamFlowRule rule = new ParamFlowRule(resourceName)
    .setParamIdx(0)
    .setCount(5);
// Set the QPS threshold for the parameter PARAM_B of the int type to 10 instead of the global threshold 5.
ParamFlowItem item = new ParamFlowItem().setObject(String.valueOf(PARAM_B))
    .setClassType(int.class.getName())
    .setCount(10);
rule.setParamFlowItemList(Collections.singletonList(item));

ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
Copy the code

3.4 Reference: github.com/alibaba/Sen…

Four, system rules

4.1 configuration 4.2 parameter 4.3 Code Configuration Examples

private void initSystemRule(a) {
    List<SystemRule> rules = new ArrayList<>();
    SystemRule rule = new SystemRule();
    rule.setHighestSystemLoad(10);
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
}
Copy the code

4.4 Reference: github.com/alibaba/Sen…

5. Authorization Rules

5.1 configuration 5.2 parameter 5.3 Code Configuration Examples

AuthorityRule rule = new AuthorityRule();
rule.setResource("test");
rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
rule.setLimitApp("appA,appB");
AuthorityRuleManager.loadRules(Collections.singletonList(rule));
Copy the code

5.4 Reference: github.com/alibaba/Sen…

Summary of the SentinelResource annotation properties

In versions earlier than 1.6.0, the Fallback function only handles DegradeException, not service exception. If blockHandler and Fallback are configured, only the blockHandler processing logic is entered when a BlockException is thrown due to traffic limiting demotion. If blockHandler, FallBack, and defaultFallback are not configured, BlockException will be thrown when traffic limiting is degraded. Starting from version 1.4.0, annotated resources support automatic statistics of service exceptions. You do not need to manually call tracer.trace (ex) to record service exceptions. Sentinel versions prior to 1.4.0 required a call to tracer.trace (ex) to log service exceptions.

Spring Cloud Stream

Schematic diagram:

Destination Binder

The component that communicates with the external messaging system provides two methods for constructing a Binding, bindConsumer and bindProducer, which are used to construct producers and consumers, respectively. Binder enables Spring Cloud Stream applications to flexibly connect to middleware. Currently, Spring provides binders for Kafka and RabbitMQ.

Destination Binding

Binding is a bridge between applications and messaging middleware for message consumption and production, created by Binder.

SpringCloudAlibaba — Stream+Rocketmq messaging microservice integration

And rely on

Both producers and consumers need to add

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
</dependency>
Copy the code

annotations

Producers: The consumer changes the annotation to@EnableBinding(Sink.class)

Write the configuration

producers: consumers:

Producer code

Consumer code

SpringCloudAlibaba — Stream custom producer interface

(1) Create interface:

Create a custom producer binding for the interface, write an out() method that returns MessageChannel, and add Output to the method to specify a custom binding constant value. This constant value is consistent with the custom binding value written in the configuration file.

(2) Modify startup class:

On the launcher class, bind the interface corresponding to the custom binding you just created.

(3) Modify the configuration file:

In the configuration file, add a custom binding. Note that the binding name is consistent with the constant string defined in the corresponding interface.

(4) Code writing:

The custom interface object is first created through Autowired, and then the output() method is called through the custom interface object in the method body to send the message.

(5) Start test:

Start the message producer, access the interface, and the message is sent successfully. In the RocketMQ console you can see the message just sent under this topic. This indicates that the stream custom producer interface is successful. In this mode, you can configure several different custom interfaces to complete the sending of various business data according to your actual business needs.

(6) Common anomalies:

Ibatis. Binding exception: If mybatis is used in the project, an exception should be reported when using message queues: Org. Apache. Ibatis. Binding. BindingException, it will be written mq custom interfaces for mybatis, an error results because she couldn’t find the corresponding XML.

Solution: We just need to pinpoint the interface scan path of Mybatis and not let it scan the interface of MQ we wrote.

SpringCloudAlibaba — Stream custom consumer interface

(1) Create interface:

Create an interface for a custom consumer binding and write an input() method that returns the SubscribableChannel and add an input annotation to the method specifying a custom binding constant value. This constant value is consistent with the custom binding value written in the configuration file.

(2) Modify startup class:

On the launcher class, bind the interface corresponding to the custom binding you just created.

(3) Modify the configuration file:

In the configuration file, add a custom binding. Note that the binding name is consistent with the constant string defined in the corresponding interface.

(4) Code writing:

Create an entity class, add the Service annotation to the class, create a method in the class that takes a string parameter (which is the message body), add a StreamListener to the method, and specify the custom constants in the custom interface you just created.

(5) Start test:

Starting the message consumer prints the introspection of any existing unconsumed messages in RocketMQ, and the producer of the corresponding topic sends a message, and the consumer immediately prints the message content. This indicates that the stream custom producer interface is successful. In this mode, you can configure several different custom interfaces to complete the consumption of various business data according to your actual business needs.

SpringCloudStream integrates with RabbitMQ and KafKa

Reference: blog.csdn.net/LSY_CSDN_/a…

Spring Cloud Stream implements message filtering consumption

Condition

Set the header, such as my-header, to the value you want:

@Autowired
private Source source;

public String testStream(a) {
  this.source.output()
    .send(
    MessageBuilder
    .withPayload("Message body")
    .setHeader("my-header"."Your header")
    .build()
  );
  return "success";
}
Copy the code

consumers

@Service
@Slf4j
public class TestStreamConsumer {
    StreamListener(value = sink. INPUT,condition = "headers['my-header']==' your header'")
    public void receive(String messageBody) {
        log.info("Received message via stream: messageBody ={}", messageBody); }}Copy the code

Use the StreamListener annotation’s condition attribute, as shown in the code. If headers[‘my-header’]==’ your header’, the method body is entered.

Tags

This mode supports only RoketMQ, not Kafka/RabbitMQ

producers

@Autowired
private Source source;

public String testStream(a) {
  this.source.output()
    .send(
    MessageBuilder
    .withPayload("Message body")
    // Note: Only one tag can be set
    .setHeader(RocketMQHeaders.TAGS, "tag1")
    .build()
  );
  return "success";
}
Copy the code

consumers

  • interface
public interface MySink {
    String INPUT1 = "input1";
    String INPUT2 = "input2";

    @Input(INPUT1)
    SubscribableChannel input(a);

    @Input(INPUT2)
    SubscribableChannel input2(a);
}
Copy the code
  • annotations
@EnableBinding({MySink.class})
Copy the code
  • configuration
spring:
  cloud:
    stream:
      rocketmq:
        binder:
          name-server: 127.0. 01.: 9876
        bindings:
          input1:
            consumer:
              # indicates that input2 consumes messages with TAG1
              tags: tag1
          input2:
            consumer:
              # indicates that input2 consumes messages with tag2 or tag3
              tags: tag2||tag3
      bindings:
        input1:
          destination: test-topic
          group: test-group1
        input2:
          destination: test-topic
          group: test-group2
Copy the code

Consumer code

@Service
@Slf4j
public class MyTestStreamConsumer {
    /** * I consume messages with TAG1 **@paramMessageBody messageBody */
    @StreamListener(MySink.INPUT1)
    public void receive1(String messageBody) {
        log.info("Message with TAG1 is consumed: messageBody ={}", messageBody);
    }

    /** * I consume messages with tag1 or tag2 **@paramMessageBody messageBody */
    @StreamListener(MySink.INPUT2)
    public void receive2(String messageBody) {
        log.info("Message with tag2/tag3 is consumed: messageBody ={}", messageBody); }}Copy the code

Reference: www.itmuch.com/spring-clou…

Spring Cloud Stream + RocketMQ implements distributed transactions

Send a message

In the Spring message programming model, the RocketMQTemplate class is used to send and receive messages using RocketMQ. After integrating Spring Cloud Stream, we can use Source to send messages as follows

private finalSource source; . source.output().send( MessageBuilder .withPayload(Demo.builder().demoId(1).remark("Ha ha ha.").build())
                        .setHeader(RocketMQHeaders.TRANSACTION_ID, UUID.randomUUID().toString())
                        .setHeader("comment", JSON.toJSONString(forObject))
                        .build()
        );

Copy the code

When using rocketMQTemplate class, the fourth parameter sendMessageInTransaction method can help us to transfer object, the send method have no spare parameter of the source interface, so we will use MessageBuilder object information in the header. Since setHeader can only pass strings, we convert the object to a Json string, then pull it out of the header during the local transaction, and convert it back.

Modify the configuration

When using the rocketMQTemplate class, we use the txProducerGroup parameter of sendMessageInTransaction to set txProducerGroup information, introducing Spring Cloud After Stream, we configure this information in the configuration file. Configuration is as follows

spring:
  cloud:
    stream:
      rocketmq:
        binder:
          name-server: 127.0. 01.: 9876
        bindings:
          output:
            producer:
              transactional: true
              # txProducerGroup
              group: test-stream-rocketmq-transactional
      bindings:
        # producers
        output:
          # specified topic
          destination: test-topic
Copy the code

Local business processing

import com.alibaba.fastjson.JSON;
import com.example.study01.domain.dto.DemoComment;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;

@Slf4j
@RocketMQTransactionListener(txProducerGroup = "test-stream-rocketmq-transactional")
public class demoTransactionalListener implements RocketMQLocalTransactionListener {
    /** * handle local transactions */
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message message, Object arg) {
        / / the message header
        MessageHeaders headers = message.getHeaders();
        String transactionalId = (String) headers.get(RocketMQHeaders.TRANSACTION_ID);
        DemoComment comment = JSON.parseObject(headers.get("comment").toString(), DemoComment.class);

        try {
            log.info("1111111");
            // Local business
            return RocketMQLocalTransactionState.COMMIT;
        } catch (Exception e) {
            returnRocketMQLocalTransactionState.ROLLBACK; }}/** * MQ Server will send check messages */ to each producer in the same group if a secondary confirmation message is missing during the execution of a local transaction or if the producer is in a waiting state
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message message) {
        log.info("222222");
        try {
            // Check the service
            return RocketMQLocalTransactionState.COMMIT;
        } catch (Exception e) {
            returnRocketMQLocalTransactionState.ROLLBACK; }}}Copy the code