diff --git a/pom.xml b/pom.xml index bd0b9c847..30dd78f5c 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,7 @@ spring-cloud-alibaba-sentinel-datasource spring-cloud-alibaba-nacos-config spring-cloud-alibaba-nacos-discovery + spring-cloud-alibaba-fescar spring-cloud-stream-binder-rocketmq spring-cloud-alibaba-nacos-config-server spring-cloud-alicloud-context diff --git a/spring-cloud-alibaba-dependencies/pom.xml b/spring-cloud-alibaba-dependencies/pom.xml index b2b9f8069..16b0582c1 100644 --- a/spring-cloud-alibaba-dependencies/pom.xml +++ b/spring-cloud-alibaba-dependencies/pom.xml @@ -19,6 +19,7 @@ 1.4.1 3.1.0 + 0.1.3 0.6.2 0.6.1 1.0.8 @@ -176,6 +177,29 @@ + + + + com.alibaba.fescar + fescar-core + ${fescar.version} + + + com.alibaba.fescar + fescar-common + ${fescar.version} + + + com.alibaba.fescar + fescar-spring + ${fescar.version} + + + com.alibaba.fescar + fescar-rm-datasource + ${fescar.version} + + com.aliyun.oss @@ -215,6 +239,11 @@ spring-cloud-alibaba-nacos-config-server ${project.version} + + org.springframework.cloud + spring-cloud-alibaba-fescar + ${project.version} + org.springframework.cloud spring-cloud-alicloud-acm @@ -258,6 +287,12 @@ ${project.version} + + org.springframework.cloud + spring-cloud-starter-alibaba-fescar + ${project.version} + + org.springframework.cloud spring-cloud-starter-alibaba-nacos-discovery diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml new file mode 100644 index 000000000..219187bcf --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/account-service/pom.xml @@ -0,0 +1,63 @@ + + + + spring-cloud-alibaba-examples + org.springframework.cloud + 0.2.2.BUILD-SNAPSHOT + ../../pom.xml + + 4.0.0 + account-service + + + + org.springframework.cloud + spring-cloud-starter-alibaba-fescar + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-jdbc + + + + mysql + mysql-connector-java + + + + + + + + org.codehaus.mojo + cobertura-maven-plugin + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.codehaus.mojo + cobertura-maven-plugin + false + + true + + + + + + \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountApplication.java b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountApplication.java new file mode 100644 index 000000000..893d34642 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author xiaojing + */ +@SpringBootApplication +public class AccountApplication { + + public static void main(String[] args) { + SpringApplication.run(AccountApplication.class, args); + } + +} diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountController.java b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountController.java new file mode 100644 index 000000000..3738fb87e --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/AccountController.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cloud.alibaba.cloud.examples; + +import java.util.Random; + +import com.alibaba.fescar.core.context.RootContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author xiaojing + */ +@RestController +public class AccountController { + + private static final Logger LOGGER = LoggerFactory.getLogger(AccountController.class); + + private static final String SUCCESS = "SUCCESS"; + private static final String FAIL = "FAIL"; + + private final JdbcTemplate jdbcTemplate; + private Random random; + + public AccountController(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + this.random = new Random(); + } + + @RequestMapping(value = "/account", method = RequestMethod.POST, produces = "application/json") + public String account(String userId, int money) { + LOGGER.info("Account Service ... xid: " + RootContext.getXID()); + + if (random.nextBoolean()) { + throw new RuntimeException("this is a mock Exception"); + } + + int result = jdbcTemplate.update( + "update account_tbl set money = money - ? where user_id = ?", + new Object[] { money, userId }); + LOGGER.info("Account Service End ... "); + if (result == 1) { + return SUCCESS; + } + return FAIL; + } + +} diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java new file mode 100644 index 000000000..62e11cd69 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cloud.alibaba.cloud.examples; + +import java.sql.SQLException; +import java.util.Random; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.jdbc.core.JdbcTemplate; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.fescar.rm.datasource.DataSourceProxy; + +/** + * @author xiaojing + */ +@Configuration +public class DatabaseConfiguration { + + private final ApplicationContext applicationContext; + + public DatabaseConfiguration(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Bean(initMethod = "init", destroyMethod = "close") + public DruidDataSource storageDataSource() throws SQLException { + + Environment environment = applicationContext.getEnvironment(); + + String ip = environment.getProperty("mysql.server.ip"); + String port = environment.getProperty("mysql.server.port"); + String dbName = environment.getProperty("mysql.db.name"); + + String userName = environment.getProperty("mysql.user.name"); + String password = environment.getProperty("mysql.user.password"); + + DruidDataSource druidDataSource = new DruidDataSource(); + druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName); + druidDataSource.setUsername(userName); + druidDataSource.setPassword(password); + druidDataSource.setDriverClassName("com.mysql.jdbc.Driver"); + druidDataSource.setInitialSize(0); + druidDataSource.setMaxActive(180); + druidDataSource.setMaxWait(60000); + druidDataSource.setMinIdle(0); + druidDataSource.setValidationQuery("Select 'x' from DUAL"); + druidDataSource.setTestOnBorrow(false); + druidDataSource.setTestOnReturn(false); + druidDataSource.setTestWhileIdle(true); + druidDataSource.setTimeBetweenEvictionRunsMillis(60000); + druidDataSource.setMinEvictableIdleTimeMillis(25200000); + druidDataSource.setRemoveAbandoned(true); + druidDataSource.setRemoveAbandonedTimeout(1800); + druidDataSource.setLogAbandoned(true); + druidDataSource.setFilters("mergeStat"); + return druidDataSource; + } + + @Bean + public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) { + return new DataSourceProxy(druidDataSource); + } + + @Bean + public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy); + + jdbcTemplate.update("delete from account_tbl where user_id = 'U100001'"); + jdbcTemplate.update( + "insert into account_tbl(user_id, money) values ('U100001', 10000)"); + + return jdbcTemplate; + } + +} diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.conf new file mode 100644 index 000000000..b264dd9da --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.conf @@ -0,0 +1,30 @@ +transport { + # tcp udt unix-domain-socket + type = "TCP" + #NIO NATIVE + server = "NIO" + #thread factory for netty + thread-factory { + boss-thread-prefix = "NettyBoss" + worker-thread-prefix = "NettyServerNIOWorker" + server-executor-thread-prefix = "NettyServerBizHandler" + share-boss-worker = false + client-selector-thread-prefix = "NettyClientSelector" + client-selector-thread-size = 1 + client-worker-thread-prefix = "NettyClientWorkerThread" + # netty boss thread size,will not be used for UDT + boss-thread-size = 1 + #auto default pin or 8 + worker-thread-size = 8 + } +} +service { + #vgroup->rgroup + vgroup_mapping.account-service-fescar-service-group = "localRgroup" + #only support single node + localRgroup.grouplist = "127.0.0.1:8091" + #degrade current not support + enableDegrade = false + #disable + disable = false +} diff --git a/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.properties b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.properties new file mode 100644 index 000000000..10f45c59a --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/account-service/src/main/resources/application.properties @@ -0,0 +1,9 @@ +spring.application.name=account-service +server.port=18084 + +mysql.server.ip=127.0.0.1 +mysql.server.port=3306 +mysql.db.name=demo + +mysql.user.name=xxxxx +mysql.user.password=xxxxx \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml new file mode 100644 index 000000000..6d77872a2 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/business-service/pom.xml @@ -0,0 +1,62 @@ + + + + spring-cloud-alibaba-examples + org.springframework.cloud + 0.2.2.BUILD-SNAPSHOT + ../../pom.xml + + 4.0.0 + business-service + + + + org.springframework.cloud + spring-cloud-starter-alibaba-fescar + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-alibaba-sentinel + + + + + + + + org.codehaus.mojo + cobertura-maven-plugin + + + + + + org.codehaus.mojo + cobertura-maven-plugin + false + + true + + + + + + \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/BusinessApplication.java b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/BusinessApplication.java new file mode 100644 index 000000000..c0ed45fc2 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/BusinessApplication.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.client.RestTemplate; + +/** + * @author xiaojing + */ +@SpringBootApplication +@EnableFeignClients +public class BusinessApplication { + + public static void main(String[] args) { + SpringApplication.run(BusinessApplication.class, args); + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + + @FeignClient(value = "storage", url = "http://127.0.0.1:18082") + public interface StorageService { + + @RequestMapping(path = "/storage/{commodityCode}/{count}") + String storage(@RequestParam("commodityCode") String commodityCode, + @RequestParam("count") int count); + + } + + @FeignClient(value = "order", url = "http://127.0.0.1:18083") + public interface OrderService { + + @RequestMapping(path = "/order", method = RequestMethod.POST) + String order(@RequestParam("userId") String userId, + @RequestParam("commodityCode") String commodityCode, + @RequestParam("orderCount") int orderCount); + + } +} diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/HomeController.java b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/HomeController.java new file mode 100644 index 000000000..039074ec0 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/HomeController.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import com.alibaba.fescar.spring.annotation.GlobalTransactional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.alibaba.cloud.examples.BusinessApplication.OrderService; +import org.springframework.cloud.alibaba.cloud.examples.BusinessApplication.StorageService; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * @author xiaojing + */ +@RestController +public class HomeController { + + private static final Logger LOGGER = LoggerFactory.getLogger(HomeController.class); + + private static final String SUCCESS = "SUCCESS"; + private static final String FAIL = "FAIL"; + private static final String USER_ID = "U100001"; + private static final String COMMODITY_CODE = "C00321"; + private static final int ORDER_COUNT = 2; + + private final RestTemplate restTemplate; + private final OrderService orderService; + private final StorageService storageService; + + public HomeController(RestTemplate restTemplate, OrderService orderService, + StorageService storageService) { + this.restTemplate = restTemplate; + this.orderService = orderService; + this.storageService = storageService; + } + + @GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-demo-tx") + @RequestMapping(value = "/fescar/rest", method = RequestMethod.GET, produces = "application/json") + public String rest() { + + String result = restTemplate.getForObject( + "http://127.0.0.1:18082/storage/" + COMMODITY_CODE + "/" + ORDER_COUNT, + String.class); + + if (!SUCCESS.equals(result)) { + throw new RuntimeException(); + } + + String url = "http://127.0.0.1:18083/order"; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap(); + map.add("userId", USER_ID); + map.add("commodityCode", COMMODITY_CODE); + map.add("orderCount", ORDER_COUNT + ""); + + HttpEntity> request = new HttpEntity>( + map, headers); + + ResponseEntity response = restTemplate.postForEntity(url, request, + String.class); + + result = response.getBody(); + + if (!SUCCESS.equals(result)) { + throw new RuntimeException(); + } + + return SUCCESS; + } + + @GlobalTransactional(timeoutMills = 300000, name = "spring-cloud-demo-tx") + @RequestMapping(value = "/fescar/feign", method = RequestMethod.GET, produces = "application/json") + public String feign() { + + String result = storageService.storage(COMMODITY_CODE, ORDER_COUNT); + + if (!SUCCESS.equals(result)) { + throw new RuntimeException(); + } + + result = orderService.order(USER_ID, COMMODITY_CODE, ORDER_COUNT); + + if (!SUCCESS.equals(result)) { + throw new RuntimeException(); + } + + return SUCCESS; + + } + +} diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java new file mode 100644 index 000000000..6594793a3 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import java.io.Serializable; + +public class Order implements Serializable { + public long id; + public String userId; + public String commodityCode; + public int count; + public int money; + + @Override + public String toString() { + return "Order{" + "id=" + id + ", userId='" + userId + '\'' + ", commodityCode='" + + commodityCode + '\'' + ", count=" + count + ", money=" + money + '}'; + } +} diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.conf new file mode 100644 index 000000000..65b695084 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.conf @@ -0,0 +1,30 @@ +transport { + # tcp udt unix-domain-socket + type = "TCP" + #NIO NATIVE + server = "NIO" + #thread factory for netty + thread-factory { + boss-thread-prefix = "NettyBoss" + worker-thread-prefix = "NettyServerNIOWorker" + server-executor-thread-prefix = "NettyServerBizHandler" + share-boss-worker = false + client-selector-thread-prefix = "NettyClientSelector" + client-selector-thread-size = 1 + client-worker-thread-prefix = "NettyClientWorkerThread" + # netty boss thread size,will not be used for UDT + boss-thread-size = 1 + #auto default pin or 8 + worker-thread-size = 8 + } +} +service { + #vgroup->rgroup + vgroup_mapping.business-service-fescar-service-group = "localRgroup" + #only support single node + localRgroup.grouplist = "127.0.0.1:8091" + #degrade current not support + enableDegrade = false + #disable + disable = false +} diff --git a/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.properties b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.properties new file mode 100644 index 000000000..1832ce6f5 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/business-service/src/main/resources/application.properties @@ -0,0 +1,8 @@ +server.port=18081 +spring.application.name=business-service +# The following configuration can be omitted. + +#feign.hystrix.enabled=true +#feign.sentinel.enabled=true + +logging.level.com.alibaba.fescar=debug \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml new file mode 100644 index 000000000..7840c5462 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/pom.xml @@ -0,0 +1,63 @@ + + + + spring-cloud-alibaba-examples + org.springframework.cloud + 0.2.2.BUILD-SNAPSHOT + ../../pom.xml + + 4.0.0 + order-service + + + + org.springframework.cloud + spring-cloud-starter-alibaba-fescar + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-starter-jdbc + + + + mysql + mysql-connector-java + + + + + + + + org.codehaus.mojo + cobertura-maven-plugin + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.codehaus.mojo + cobertura-maven-plugin + false + + true + + + + + + \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java new file mode 100644 index 000000000..8e5c7d87b --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cloud.alibaba.cloud.examples; + +import java.sql.SQLException; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.jdbc.core.JdbcTemplate; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.fescar.rm.datasource.DataSourceProxy; + +/** + * @author xiaojing + */ +@Configuration +public class DatabaseConfiguration { + + private final ApplicationContext applicationContext; + + public DatabaseConfiguration(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Bean(initMethod = "init", destroyMethod = "close") + public DruidDataSource storageDataSource() throws SQLException { + + Environment env = applicationContext.getEnvironment(); + + String ip = env.getProperty("mysql.server.ip"); + String port = env.getProperty("mysql.server.port"); + String dbName = env.getProperty("mysql.db.name"); + + String userName = env.getProperty("mysql.user.name"); + String password = env.getProperty("mysql.user.password"); + + DruidDataSource druidDataSource = new DruidDataSource(); + druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName); + druidDataSource.setUsername(userName); + druidDataSource.setPassword(password); + druidDataSource.setDriverClassName("com.mysql.jdbc.Driver"); + druidDataSource.setInitialSize(0); + druidDataSource.setMaxActive(180); + druidDataSource.setMaxWait(60000); + druidDataSource.setMinIdle(0); + druidDataSource.setValidationQuery("Select 'x' from DUAL"); + druidDataSource.setTestOnBorrow(false); + druidDataSource.setTestOnReturn(false); + druidDataSource.setTestWhileIdle(true); + druidDataSource.setTimeBetweenEvictionRunsMillis(60000); + druidDataSource.setMinEvictableIdleTimeMillis(25200000); + druidDataSource.setRemoveAbandoned(true); + druidDataSource.setRemoveAbandonedTimeout(1800); + druidDataSource.setLogAbandoned(true); + druidDataSource.setFilters("mergeStat"); + return druidDataSource; + } + + @Bean + public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) { + return new DataSourceProxy(druidDataSource); + } + + @Bean + public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) { + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy); + + jdbcTemplate.execute("TRUNCATE TABLE order_tbl"); + + return jdbcTemplate; + } + +} diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OderApplication.java b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OderApplication.java new file mode 100644 index 000000000..1ab0dab11 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OderApplication.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.web.client.RestTemplate; + +/** + * @author xiaojing + */ +@SpringBootApplication +public class OderApplication { + + public static void main(String[] args) { + SpringApplication.run(OderApplication.class, args); + } + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } + +} diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java new file mode 100644 index 000000000..6594793a3 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/Order.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import java.io.Serializable; + +public class Order implements Serializable { + public long id; + public String userId; + public String commodityCode; + public int count; + public int money; + + @Override + public String toString() { + return "Order{" + "id=" + id + ", userId='" + userId + '\'' + ", commodityCode='" + + commodityCode + '\'' + ", count=" + count + ", money=" + money + '}'; + } +} diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OrderController.java b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OrderController.java new file mode 100644 index 000000000..2e0e0f81c --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/OrderController.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.Random; + +import com.alibaba.fescar.core.context.RootContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.PreparedStatementCreator; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.client.RestTemplate; + +/** + * @author xiaojing + */ +@RestController +public class OrderController { + + private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class); + private static final String SUCCESS = "SUCCESS"; + private static final String FAIL = "FAIL"; + private static final String USER_ID = "U100001"; + private static final String COMMODITY_CODE = "C00321"; + + private final JdbcTemplate jdbcTemplate; + private final RestTemplate restTemplate; + private Random random; + + public OrderController(JdbcTemplate jdbcTemplate, RestTemplate restTemplate) { + this.jdbcTemplate = jdbcTemplate; + this.restTemplate = restTemplate; + this.random = new Random(); + } + + @RequestMapping(value = "/order", method = RequestMethod.POST, produces = "application/json") + public String order(String userId, String commodityCode, int orderCount) { + LOGGER.info("Order Service Begin ... xid: " + RootContext.getXID()); + + int orderMoney = calculate(commodityCode, orderCount); + + invokerAccountService(orderMoney); + + final Order order = new Order(); + order.userId = userId; + order.commodityCode = commodityCode; + order.count = orderCount; + order.money = orderMoney; + + KeyHolder keyHolder = new GeneratedKeyHolder(); + + int result = jdbcTemplate.update(new PreparedStatementCreator() { + + @Override + public PreparedStatement createPreparedStatement(Connection con) + throws SQLException { + PreparedStatement pst = con.prepareStatement( + "insert into order_tbl (user_id, commodity_code, count, money) values (?, ?, ?, ?)", + PreparedStatement.RETURN_GENERATED_KEYS); + pst.setObject(1, order.userId); + pst.setObject(2, order.commodityCode); + pst.setObject(3, order.count); + pst.setObject(4, order.money); + return pst; + } + }, keyHolder); + + order.id = (long) keyHolder.getKey(); + + if (random.nextBoolean()) { + throw new RuntimeException("this is a mock Exception"); + } + + LOGGER.info("Order Service End ... Created " + order); + + if (result == 1) { + return SUCCESS; + } + return FAIL; + } + + private int calculate(String commodityId, int orderCount) { + return 2 * orderCount; + } + + private void invokerAccountService(int orderMoney) { + String url = "http://127.0.0.1:18084/account"; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap(); + + map.add("userId", USER_ID); + map.add("money", orderMoney + ""); + + HttpEntity> request = new HttpEntity>( + map, headers); + + ResponseEntity response = restTemplate.postForEntity(url, request, + String.class); + } +} diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.conf new file mode 100644 index 000000000..c298ca0c2 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.conf @@ -0,0 +1,30 @@ +transport { + # tcp udt unix-domain-socket + type = "TCP" + #NIO NATIVE + server = "NIO" + #thread factory for netty + thread-factory { + boss-thread-prefix = "NettyBoss" + worker-thread-prefix = "NettyServerNIOWorker" + server-executor-thread-prefix = "NettyServerBizHandler" + share-boss-worker = false + client-selector-thread-prefix = "NettyClientSelector" + client-selector-thread-size = 1 + client-worker-thread-prefix = "NettyClientWorkerThread" + # netty boss thread size,will not be used for UDT + boss-thread-size = 1 + #auto default pin or 8 + worker-thread-size = 8 + } +} +service { + #vgroup->rgroup + vgroup_mapping.order-service-fescar-service-group = "localRgroup" + #only support single node + localRgroup.grouplist = "127.0.0.1:8091" + #degrade current not support + enableDegrade = false + #disable + disable = false +} diff --git a/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.properties b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.properties new file mode 100644 index 000000000..f3e417d8d --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/order-service/src/main/resources/application.properties @@ -0,0 +1,9 @@ +spring.application.name=order-service +server.port=18083 + +mysql.server.ip=127.0.0.1 +mysql.server.port=3306 +mysql.db.name=demo + +mysql.user.name=xxxxx +mysql.user.password=xxxxx \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/readme-zh.md b/spring-cloud-alibaba-examples/fescar-example/readme-zh.md new file mode 100644 index 000000000..be3919497 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/readme-zh.md @@ -0,0 +1,156 @@ +# Fescar Example + +## 项目说明 + + +本项目演示如何使用 Fescar Starter 完成 Spring Cloud 应用的分布式事务接入。 + +[Fescar](https://github.com/alibaba/fescar) 是 阿里巴巴 开源的 分布式事务中间件,以 高效 并且对业务 0 侵入 的方式,解决 微服务 场景下面临的分布式事务问题。 + + + +## 准备工作 + +在运行此示例之前,你需要先完成如下几步准备工作: + +1. 配置数据库 + +1. Create UNDO_LOG table + +1. Create tables for examples business + +1. Start Fescar Server + + +### 配置数据库 + +首先,你需要有一个支持 InnoDB 引擎的 MySQL 数据库。 + +**注意**: 实际上,Fescar 支持不同的应用使用完全不相干的数据库,但是这里为了简单地演示一个原理,所以我们选择了只使用一个数据库。 + +将 `account-server`、`order-service`、`storage-service` 这三个应用中的 resources 目录下的 `application.properties` 文件中的如下配置修改成你运行环境中的实际配置。 + +``` +mysql.server.ip=your mysql server ip address +mysql.server.port=your mysql server listening port +mysql.db.name=your database name for test + +mysql.user.name=your mysql server username +mysql.user.password=your mysql server password + +``` + +### 创建 UNDO_LOG 表 + +[Fescar AT 模式]() 需要使用到 UNDO_LOG 表。 + +``` $sql +CREATE TABLE `undo_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `branch_id` bigint(20) NOT NULL, + `xid` varchar(100) NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int(11) NOT NULL, + `log_created` datetime NOT NULL, + `log_modified` datetime NOT NULL, + `ext` varchar(100) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_unionkey` (`xid`,`branch_id`) +) ENGINE=InnoDB AUTO_INCREMENT=159 DEFAULT CHARSET=utf8 +``` + +### 创建 示例中 业务所需要的数据库表 + +```$sql +DROP TABLE IF EXISTS `storage_tbl`; +CREATE TABLE `storage_tbl` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `commodity_code` varchar(255) DEFAULT NULL, + `count` int(11) DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY (`commodity_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS `order_tbl`; +CREATE TABLE `order_tbl` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` varchar(255) DEFAULT NULL, + `commodity_code` varchar(255) DEFAULT NULL, + `count` int(11) DEFAULT 0, + `money` int(11) DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS `account_tbl`; +CREATE TABLE `account_tbl` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` varchar(255) DEFAULT NULL, + `money` int(11) DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +``` + +### 启动 Fescar Server + +点击这个页面 [https://github.com/alibaba/fescar/releases](https://github.com/alibaba/fescar/releases),下载最新版本的 Fescar Server 端. + + +进入解压之后的 bin 目录,执行如下命令来启动 + +```$shell +sh fescar-server.sh $LISTEN_PORT $PATH_FOR_PERSISTENT_DATA +``` + +在这个示例中,采用如下命令来启动 Fescar Server + +```$shell +sh fescar-server.sh 8091 ~/fescar/data/ +``` + +**注意** 如果你修改了端口号,那么记得需要在各个示例工程中的 `application.conf` 文件中,修改 grouplist 的值。 + + +## 运行示例 + +分别运行 `account-server`、`order-service`、`storage-service` 和 `business-service` 这三个应用的 Main 函数,启动示例。 + +启动示例后,通过 HTTP 的 GET 方法访问如下两个 URL,可以分别验证在 `business-service` 中 通过 RestTemplate 和 FeignClient 调用其他服务的场景。 + +```$xslt +http://127.0.0.1:18081/fescar/feign + +http://127.0.0.1:18081/fescar/rest +``` + +## 如何验证分布式事务成功? + +### Xid 信息是否成功传递 + +在 `account-server`、`order-service` 和 `storage-service` 三个 服务的 Controller 中,第一个执行的逻辑都是输出 RootContext 中的 Xid 信息,如果看到都输出了正确的 Xid 信息,即每次都发生变化,且同一次调用中所有服务的 Xid 都一致。则表明 Fescar 的 Xid 的传递和还原是正常的。 + +### 数据库中数据是否一致 + +在本示例中,我们模拟了一个用户购买货物的场景,StorageService 负责扣减库存数量,OrderService 负责保存订单,AccountService 负责扣减用户账户余额。 + +为了演示样例,我们在 OrderService 和 AccountService 中 使用 Random.nextBoolean() 的方式来随机抛出异常,模拟了在服务调用时随机发生异常的场景。 + +如果分布式事务生效的话, 那么以下等式应该成立 + + +- 用户原始金额(1000) = 用户现存的金额 + 货物单价 (2) * 订单数量 * 每单的货物数量(2) + +- 货物的初始数量(100) = 货物的现存数量 + 订单数量 * 每单的货物数量(2) + +## 对 Spring Cloud 支持点 + +- 通过 Spring MVC 提供服务的服务提供者,在收到 header 中含有 Fescar 信息的 HTTP 请求时,可以自动还原 Fescar 上下文。 + +- 支持服务调用者通过 RestTemplate 调用时,自动传递 Fescar 上下文。 + +- 支持服务调用者通过 FeignClient 调用时,自动传递 Fescar 上下文。 + +- 支持 FeignClient 和 Hystrix 同时使用的场景。 + +- 支持 FeignClient 和 Sentinel 同时使用的场景。 \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml b/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml new file mode 100644 index 000000000..bcdca22c0 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/pom.xml @@ -0,0 +1,68 @@ + + + + spring-cloud-alibaba-examples + org.springframework.cloud + 0.2.2.BUILD-SNAPSHOT + ../../pom.xml + + 4.0.0 + storage-service + + + + org.springframework.cloud + spring-cloud-starter-alibaba-fescar + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + com.alibaba + druid + 1.1.10 + + + org.springframework.boot + spring-boot-starter-jdbc + + + + mysql + mysql-connector-java + + + + + + + + org.codehaus.mojo + cobertura-maven-plugin + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.codehaus.mojo + cobertura-maven-plugin + false + + true + + + + + + \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java new file mode 100644 index 000000000..2973f21ae --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/DatabaseConfiguration.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import java.sql.SQLException; + +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.fescar.rm.datasource.DataSourceProxy; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.jdbc.core.JdbcTemplate; + +/** + * @author xiaojing + */ +@Configuration +public class DatabaseConfiguration { + + private final ApplicationContext applicationContext; + + public DatabaseConfiguration(ApplicationContext applicationContext) { + this.applicationContext = applicationContext; + } + + @Bean(initMethod = "init", destroyMethod = "close") + public DruidDataSource storageDataSource() throws SQLException { + + Environment environment = applicationContext.getEnvironment(); + + String ip = environment.getProperty("mysql.server.ip"); + String port = environment.getProperty("mysql.server.port"); + String dbName = environment.getProperty("mysql.db.name"); + + String userName = environment.getProperty("mysql.user.name"); + String password = environment.getProperty("mysql.user.password"); + + DruidDataSource druidDataSource = new DruidDataSource(); + druidDataSource.setUrl("jdbc:mysql://" + ip + ":" + port + "/" + dbName); + druidDataSource.setUsername(userName); + druidDataSource.setPassword(password); + druidDataSource.setDriverClassName("com.mysql.jdbc.Driver"); + druidDataSource.setInitialSize(0); + druidDataSource.setMaxActive(180); + druidDataSource.setMaxWait(60000); + druidDataSource.setMinIdle(0); + druidDataSource.setValidationQuery("Select 'x' from DUAL"); + druidDataSource.setTestOnBorrow(false); + druidDataSource.setTestOnReturn(false); + druidDataSource.setTestWhileIdle(true); + druidDataSource.setTimeBetweenEvictionRunsMillis(60000); + druidDataSource.setMinEvictableIdleTimeMillis(25200000); + druidDataSource.setRemoveAbandoned(true); + druidDataSource.setRemoveAbandonedTimeout(1800); + druidDataSource.setLogAbandoned(true); + druidDataSource.setFilters("mergeStat"); + return druidDataSource; + } + + @Bean + public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) { + return new DataSourceProxy(druidDataSource); + } + + @Bean + public JdbcTemplate jdbcTemplate(DataSourceProxy dataSourceProxy) { + + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceProxy); + + jdbcTemplate.update("delete from storage_tbl where commodity_code = 'C00321'"); + jdbcTemplate.update( + "insert into storage_tbl(commodity_code, count) values ('C00321', 100)"); + + return jdbcTemplate; + + } + +} diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageApplication.java b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageApplication.java new file mode 100644 index 000000000..94bbac7f4 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageApplication.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * @author xiaojing + */ +@SpringBootApplication +public class StorageApplication { + + public static void main(String[] args) { + SpringApplication.run(StorageApplication.class, args); + } + +} diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageController.java b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageController.java new file mode 100644 index 000000000..fd97ab2c8 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/java/org/springframework/cloud/alibaba/cloud/examples/StorageController.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.cloud.examples; + +import com.alibaba.fescar.core.context.RootContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author xiaojing + */ +@RestController +public class StorageController { + + private static final Logger LOGGER = LoggerFactory.getLogger(StorageController.class); + + private static final String SUCCESS = "SUCCESS"; + private static final String FAIL = "FAIL"; + + private final JdbcTemplate jdbcTemplate; + + public StorageController(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @RequestMapping(value = "/storage/{commodityCode}/{count}", method = RequestMethod.GET, produces = "application/json") + public String echo(@PathVariable String commodityCode, @PathVariable int count) { + LOGGER.info("Storage Service Begin ... xid: " + RootContext.getXID()); + int result = jdbcTemplate.update( + "update storage_tbl set count = count - ? where commodity_code = ?", + new Object[] { count, commodityCode }); + LOGGER.info("Storage Service End ... "); + if (result == 1) { + return SUCCESS; + } + return FAIL; + } +} diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.conf b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.conf new file mode 100644 index 000000000..4bc5989e1 --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.conf @@ -0,0 +1,30 @@ +transport { + # tcp udt unix-domain-socket + type = "TCP" + #NIO NATIVE + server = "NIO" + #thread factory for netty + thread-factory { + boss-thread-prefix = "NettyBoss" + worker-thread-prefix = "NettyServerNIOWorker" + server-executor-thread-prefix = "NettyServerBizHandler" + share-boss-worker = false + client-selector-thread-prefix = "NettyClientSelector" + client-selector-thread-size = 1 + client-worker-thread-prefix = "NettyClientWorkerThread" + # netty boss thread size,will not be used for UDT + boss-thread-size = 1 + #auto default pin or 8 + worker-thread-size = 8 + } +} +service { + #vgroup->rgroup + vgroup_mapping.storage-service-fescar-service-group = "localRgroup" + #only support single node + localRgroup.grouplist = "127.0.0.1:8091" + #degrade current not support + enableDegrade = false + #disable + disable = false +} diff --git a/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.properties b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.properties new file mode 100644 index 000000000..832eaecdf --- /dev/null +++ b/spring-cloud-alibaba-examples/fescar-example/storage-service/src/main/resources/application.properties @@ -0,0 +1,9 @@ +spring.application.name=storage-service +server.port=18082 + +mysql.server.ip=127.0.0.1 +mysql.server.port=3306 +mysql.db.name=demo + +mysql.user.name=xxxxx +mysql.user.password=xxxxx \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/pom.xml b/spring-cloud-alibaba-examples/pom.xml index 6e2790f97..6cd8e7311 100644 --- a/spring-cloud-alibaba-examples/pom.xml +++ b/spring-cloud-alibaba-examples/pom.xml @@ -29,6 +29,10 @@ ans-example/ans-consumer-feign-example ans-example/ans-consumer-ribbon-example ans-example/ans-provider-example + fescar-example/business-service + fescar-example/order-service + fescar-example/storage-service + fescar-example/account-service acm-example/acm-local-example rocketmq-example spring-cloud-bus-rocketmq-example diff --git a/spring-cloud-alibaba-fescar/pom.xml b/spring-cloud-alibaba-fescar/pom.xml new file mode 100644 index 000000000..15613f576 --- /dev/null +++ b/spring-cloud-alibaba-fescar/pom.xml @@ -0,0 +1,117 @@ + + + + + org.springframework.cloud + spring-cloud-alibaba + 0.2.2.BUILD-SNAPSHOT + + 4.0.0 + + org.springframework.cloud + spring-cloud-alibaba-fescar + Spring Cloud Alibaba Fescar + + + + + com.alibaba.fescar + fescar-core + + + com.alibaba.fescar + fescar-common + + + com.alibaba.fescar + fescar-spring + + + com.alibaba.fescar + fescar-rm-datasource + + + + org.springframework.cloud + spring-cloud-starter-openfeign + provided + true + + + org.springframework.cloud + spring-cloud-starter-alibaba-sentinel + provided + true + + + + org.springframework.cloud + spring-cloud-commons + true + + + + org.springframework.cloud + spring-cloud-starter-netflix-ribbon + provided + true + + + + + + org.springframework.boot + spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-actuator + provided + true + + + + org.springframework.boot + spring-boot-actuator-autoconfigure + provided + true + + + + org.springframework.boot + spring-boot-configuration-processor + provided + true + + + + org.springframework.boot + spring-boot + provided + true + + + + org.springframework.boot + spring-boot-autoconfigure + provided + true + + + org.springframework.boot + spring-boot-starter-web + provided + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/FescarProperties.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/FescarProperties.java new file mode 100644 index 000000000..f7847d968 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/FescarProperties.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author xiaojing + */ +@ConfigurationProperties("spring.cloud.alibaba.fescar") +public class FescarProperties { + + // todo support config Fescar server information + + /** + * Fescar tx service group.default is ${spring.application.name}-fescar-service-group. + */ + private String txServiceGroup; + + public String getTxServiceGroup() { + return txServiceGroup; + } + + public void setTxServiceGroup(String txServiceGroup) { + this.txServiceGroup = txServiceGroup; + } + +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/GlobalTransactionAutoConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/GlobalTransactionAutoConfiguration.java new file mode 100644 index 000000000..d876ad3bd --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/GlobalTransactionAutoConfiguration.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar; + +import com.alibaba.fescar.spring.annotation.GlobalTransactionScanner; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + +/** + * @author xiaojing + */ + +@Configuration +@EnableConfigurationProperties(FescarProperties.class) +public class GlobalTransactionAutoConfiguration { + + private final ApplicationContext applicationContext; + + private final FescarProperties fescarProperties; + + public GlobalTransactionAutoConfiguration(ApplicationContext applicationContext, + FescarProperties fescarProperties) { + this.applicationContext = applicationContext; + this.fescarProperties = fescarProperties; + } + + @Bean + public GlobalTransactionScanner globalTransactionScanner() { + + String applicationName = applicationContext.getEnvironment() + .getProperty("spring.application.name"); + + String txServiceGroup = fescarProperties.getTxServiceGroup(); + + if (StringUtils.isEmpty(txServiceGroup)) { + txServiceGroup = applicationName + "-fescar-service-group"; + fescarProperties.setTxServiceGroup(txServiceGroup); + } + + return new GlobalTransactionScanner(applicationName, txServiceGroup); + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarBeanPostProcessor.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarBeanPostProcessor.java new file mode 100644 index 000000000..9d7b685c3 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarBeanPostProcessor.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; + +/** + * @author xiaojing + */ +final class FescarBeanPostProcessor implements BeanPostProcessor { + + private final FescarFeignObjectWrapper eagleEyeFeignObjectWrapper; + + FescarBeanPostProcessor(FescarFeignObjectWrapper eagleEyeFeignObjectWrapper) { + this.eagleEyeFeignObjectWrapper = eagleEyeFeignObjectWrapper; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + return this.eagleEyeFeignObjectWrapper.wrap(bean); + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + return bean; + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarContextBeanPostProcessor.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarContextBeanPostProcessor.java new file mode 100644 index 000000000..39afc2792 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarContextBeanPostProcessor.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.cloud.openfeign.FeignContext; + +/** + * @author xiaojing + */ +public class FescarContextBeanPostProcessor implements BeanPostProcessor { + + private final BeanFactory beanFactory; + private FescarFeignObjectWrapper eagleEyeFeignObjectWrapper; + + FescarContextBeanPostProcessor(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) + throws BeansException { + if (bean instanceof FeignContext && !(bean instanceof FescarFeignContext)) { + return new FescarFeignContext(getEagleEyeFeignObjectWrapper(), + (FeignContext) bean); + } + return bean; + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) + throws BeansException { + return bean; + } + + private FescarFeignObjectWrapper getEagleEyeFeignObjectWrapper() { + if (this.eagleEyeFeignObjectWrapper == null) { + this.eagleEyeFeignObjectWrapper = this.beanFactory + .getBean(FescarFeignObjectWrapper.class); + } + return this.eagleEyeFeignObjectWrapper; + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignBuilder.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignBuilder.java new file mode 100644 index 000000000..acab33ff9 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignBuilder.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import org.springframework.beans.factory.BeanFactory; + +import feign.Feign; + +/** + * @author xiaojing + */ +final class FescarFeignBuilder { + + private FescarFeignBuilder() { + } + + static Feign.Builder builder(BeanFactory beanFactory) { + return Feign.builder().client(new FescarFeignClient(beanFactory)); + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClient.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClient.java new file mode 100644 index 000000000..9fc9d527c --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClient.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.fescar.core.context.RootContext; + +import org.springframework.beans.factory.BeanFactory; + +import feign.Client; +import feign.Request; +import feign.Response; +import org.springframework.util.StringUtils; + +/** + * @author xiaojing + */ +public class FescarFeignClient implements Client { + + private final Client delegate; + private final BeanFactory beanFactory; + + FescarFeignClient(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + this.delegate = new Client.Default(null, null); + } + + FescarFeignClient(BeanFactory beanFactory, Client delegate) { + this.delegate = delegate; + this.beanFactory = beanFactory; + } + + @Override + public Response execute(Request request, Request.Options options) throws IOException { + + Request modifiedRequest = getModifyRequest(request); + + try { + return this.delegate.execute(modifiedRequest, options); + } + finally { + + } + } + + private Request getModifyRequest(Request request) { + + String xid = RootContext.getXID(); + + if (StringUtils.isEmpty(xid)) { + return request; + } + + Map> headers = new HashMap<>(); + headers.putAll(request.headers()); + + List fescarXid = new ArrayList<>(); + fescarXid.add(xid); + headers.put(RootContext.KEY_XID, fescarXid); + + return Request.create(request.method(), request.url(), headers, request.body(), + request.charset()); + } + +} \ No newline at end of file diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClientAutoConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClientAutoConfiguration.java new file mode 100644 index 000000000..cdf1f6863 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignClientAutoConfiguration.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.openfeign.FeignAutoConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Scope; + +import feign.Client; +import feign.Feign; + +/** + * @author xiaojing + */ + +@Configuration +@ConditionalOnClass(Client.class) +@AutoConfigureBefore(FeignAutoConfiguration.class) +public class FescarFeignClientAutoConfiguration { + + @Bean + @Scope("prototype") + @ConditionalOnClass(name = "com.netflix.hystrix.HystrixCommand") + @ConditionalOnProperty(name = "feign.hystrix.enabled", havingValue = "true") + Feign.Builder feignHystrixBuilder(BeanFactory beanFactory) { + return FescarHystrixFeignBuilder.builder(beanFactory); + } + + @Bean + @Scope("prototype") + @ConditionalOnClass(name = "com.alibaba.csp.sentinel.SphU") + @ConditionalOnProperty(name = "feign.sentinel.enabled", havingValue = "true") + Feign.Builder feignSentinelBuilder(BeanFactory beanFactory) { + return FescarSentinelFeignBuilder.builder(beanFactory); + } + + @Bean + @ConditionalOnMissingBean + @Scope("prototype") + Feign.Builder feignBuilder(BeanFactory beanFactory) { + return FescarFeignBuilder.builder(beanFactory); + } + + @Configuration + protected static class FeignBeanPostProcessorConfiguration { + + @Bean + FescarBeanPostProcessor eagleEyeBeanPostProcessor( + FescarFeignObjectWrapper eagleEyeFeignObjectWrapper) { + return new FescarBeanPostProcessor(eagleEyeFeignObjectWrapper); + } + + @Bean + FescarContextBeanPostProcessor eagleEyeContextBeanPostProcessor( + BeanFactory beanFactory) { + return new FescarContextBeanPostProcessor(beanFactory); + } + + @Bean + FescarFeignObjectWrapper eagleEyeFeignObjectWrapper(BeanFactory beanFactory) { + return new FescarFeignObjectWrapper(beanFactory); + } + } + +} \ No newline at end of file diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignContext.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignContext.java new file mode 100644 index 000000000..1ae5ea8d4 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignContext.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import java.util.HashMap; +import java.util.Map; + +import feign.Client; +import org.springframework.cloud.openfeign.FeignContext; + +/** + * + * @author xiaojing + */ +public class FescarFeignContext extends FeignContext { + + private final FescarFeignObjectWrapper fescarFeignObjectWrapper; + private final FeignContext delegate; + + FescarFeignContext(FescarFeignObjectWrapper eagleEyeFeignObjectWrapper, + FeignContext delegate) { + this.fescarFeignObjectWrapper = eagleEyeFeignObjectWrapper; + this.delegate = delegate; + } + + @Override + public T getInstance(String name, Class type) { + T object = this.delegate.getInstance(name, type); + if (object instanceof Client) { + return object; + } + return (T) this.fescarFeignObjectWrapper.wrap(object); + } + + @Override + public Map getInstances(String name, Class type) { + Map instances = this.delegate.getInstances(name, type); + if (instances == null) { + return null; + } + Map convertedInstances = new HashMap<>(); + for (Map.Entry entry : instances.entrySet()) { + if (entry.getValue() instanceof Client) { + convertedInstances.put(entry.getKey(), entry.getValue()); + } + else { + convertedInstances.put(entry.getKey(), + (T) this.fescarFeignObjectWrapper.wrap(entry.getValue())); + } + } + return convertedInstances; + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignObjectWrapper.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignObjectWrapper.java new file mode 100644 index 000000000..9b42b2f98 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarFeignObjectWrapper.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; + +import feign.Client; +import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; +import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; + +/** + * @author xiaojing + */ +public class FescarFeignObjectWrapper { + + private final BeanFactory beanFactory; + + private CachingSpringLoadBalancerFactory cachingSpringLoadBalancerFactory; + private SpringClientFactory springClientFactory; + + FescarFeignObjectWrapper(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + Object wrap(Object bean) { + if (bean instanceof Client && !(bean instanceof FescarFeignClient)) { + if (bean instanceof LoadBalancerFeignClient) { + LoadBalancerFeignClient client = ((LoadBalancerFeignClient) bean); + return new FescarLoadBalancerFeignClient(client.getDelegate(), factory(), + clientFactory(), this.beanFactory); + } + return new FescarFeignClient(this.beanFactory, (Client) bean); + } + return bean; + } + + CachingSpringLoadBalancerFactory factory() { + if (this.cachingSpringLoadBalancerFactory == null) { + this.cachingSpringLoadBalancerFactory = this.beanFactory + .getBean(CachingSpringLoadBalancerFactory.class); + } + return this.cachingSpringLoadBalancerFactory; + } + + SpringClientFactory clientFactory() { + if (this.springClientFactory == null) { + this.springClientFactory = this.beanFactory + .getBean(SpringClientFactory.class); + } + return this.springClientFactory; + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarHystrixFeignBuilder.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarHystrixFeignBuilder.java new file mode 100644 index 000000000..b67a9d840 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarHystrixFeignBuilder.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import feign.Retryer; +import feign.hystrix.HystrixFeign; +import org.springframework.beans.factory.BeanFactory; + +import feign.Feign; + +/** + * @author xiaojing + */ +final class FescarHystrixFeignBuilder { + + private FescarHystrixFeignBuilder() { + } + + static Feign.Builder builder(BeanFactory beanFactory) { + return HystrixFeign.builder().retryer(Retryer.NEVER_RETRY) + .client(new FescarFeignClient(beanFactory)); + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarLoadBalancerFeignClient.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarLoadBalancerFeignClient.java new file mode 100644 index 000000000..7f00d0585 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarLoadBalancerFeignClient.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import java.io.IOException; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; + +import feign.Client; +import feign.Request; +import feign.Response; +import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; +import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; + +/** + * @author xiaojing + */ +public class FescarLoadBalancerFeignClient extends LoadBalancerFeignClient { + + private final BeanFactory beanFactory; + + FescarLoadBalancerFeignClient(Client delegate, + CachingSpringLoadBalancerFactory lbClientFactory, + SpringClientFactory clientFactory, BeanFactory beanFactory) { + super(wrap(delegate, beanFactory), lbClientFactory, clientFactory); + this.beanFactory = beanFactory; + } + + @Override + public Response execute(Request request, Request.Options options) throws IOException { + return super.execute(request, options); + } + + private static Client wrap(Client delegate, BeanFactory beanFactory) { + return (Client) new FescarFeignObjectWrapper(beanFactory).wrap(delegate); + } + +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarSentinelFeignBuilder.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarSentinelFeignBuilder.java new file mode 100644 index 000000000..42f632144 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/FescarSentinelFeignBuilder.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.feign; + +import org.springframework.beans.factory.BeanFactory; + +import feign.Feign; +import feign.Retryer; +import feign.hystrix.HystrixFeign; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel; +import org.springframework.cloud.alibaba.sentinel.feign.SentinelFeign; + +/** + * @author xiaojing + */ +final class FescarSentinelFeignBuilder { + + private FescarSentinelFeignBuilder() { + } + + static Feign.Builder builder(BeanFactory beanFactory) { + return SentinelFeign.builder().retryer(Retryer.NEVER_RETRY) + .client(new FescarFeignClient(beanFactory)); + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixAutoConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixAutoConfiguration.java new file mode 100644 index 000000000..f7db4b0d9 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixAutoConfiguration.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cloud.alibaba.fescar.feign.hystrix; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.netflix.hystrix.HystrixCommand; + +/** + * @author xiaojing + */ + +@Configuration +@ConditionalOnClass(HystrixCommand.class) +public class FescarHystrixAutoConfiguration { + + @Bean + FescarHystrixConcurrencyStrategy fescarHystrixConcurrencyStrategy() { + return new FescarHystrixConcurrencyStrategy(); + } + +} \ No newline at end of file diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixConcurrencyStrategy.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixConcurrencyStrategy.java new file mode 100644 index 000000000..e21ff1680 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/feign/hystrix/FescarHystrixConcurrencyStrategy.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.cloud.alibaba.fescar.feign.hystrix; + +import java.util.concurrent.Callable; + +import com.alibaba.fescar.core.context.RootContext; + +import com.netflix.hystrix.strategy.HystrixPlugins; +import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; + +/** + * @author xiaojing + */ +public class FescarHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy { + + private HystrixConcurrencyStrategy delegate; + + public FescarHystrixConcurrencyStrategy() { + this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy(); + HystrixPlugins.reset(); + HystrixPlugins.getInstance().registerConcurrencyStrategy(this); + } + + @Override + public Callable wrapCallable(Callable c) { + if (c instanceof FescarContextCallable) { + return c; + } + + Callable wrappedCallable; + if (this.delegate != null) { + wrappedCallable = this.delegate.wrapCallable(c); + } + else { + wrappedCallable = c; + } + if (wrappedCallable instanceof FescarContextCallable) { + return wrappedCallable; + } + + return new FescarContextCallable<>(wrappedCallable); + } + + private static class FescarContextCallable implements Callable { + + private final Callable actual; + private final String xid; + + FescarContextCallable(Callable actual) { + this.actual = actual; + this.xid = RootContext.getXID(); + } + + @Override + public K call() throws Exception { + try { + RootContext.bind(xid); + return actual.call(); + } + finally { + RootContext.unbind(); + } + } + + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateAutoConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateAutoConfiguration.java new file mode 100644 index 000000000..91191ed0d --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateAutoConfiguration.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.rest; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.web.client.RestTemplate; + +/** + * @author xiaojing + */ + +@Configuration +public class FescarRestTemplateAutoConfiguration { + + @Bean + public FescarRestTemplateInterceptor fescarRestTemplateInterceptor() { + return new FescarRestTemplateInterceptor(); + } + + @Autowired(required = false) + private Collection restTemplates; + + @Autowired + private FescarRestTemplateInterceptor fescarRestTemplateInterceptor; + + @PostConstruct + public void init() { + if (this.restTemplates != null) { + for (RestTemplate restTemplate : restTemplates) { + List interceptors = new ArrayList( + restTemplate.getInterceptors()); + interceptors.add(this.fescarRestTemplateInterceptor); + restTemplate.setInterceptors(interceptors); + } + } + } + +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateInterceptor.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateInterceptor.java new file mode 100644 index 000000000..5a5b2a10a --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/rest/FescarRestTemplateInterceptor.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.rest; + +import java.io.IOException; + +import com.alibaba.fescar.core.context.RootContext; + +import org.springframework.http.HttpRequest; +import org.springframework.http.client.ClientHttpRequestExecution; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.http.client.support.HttpRequestWrapper; +import org.springframework.util.StringUtils; + +/** + * @author xiaojing + */ +public class FescarRestTemplateInterceptor implements ClientHttpRequestInterceptor { + @Override + public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, + ClientHttpRequestExecution clientHttpRequestExecution) throws IOException { + HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest); + + String xid = RootContext.getXID(); + + if (!StringUtils.isEmpty(xid)) { + requestWrapper.getHeaders().add(RootContext.KEY_XID, xid); + } + return clientHttpRequestExecution.execute(requestWrapper, bytes); + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptor.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptor.java new file mode 100644 index 000000000..84ffee8e8 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptor.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.web; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.alibaba.fescar.core.context.RootContext; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; +import org.springframework.web.servlet.HandlerInterceptor; + +/** + * @author xiaojing + * + * Fescar HandlerInterceptor, Convert Fescar information into + * @see com.alibaba.fescar.core.context.RootContext from http request's header in + * {@link org.springframework.web.servlet.HandlerInterceptor#preHandle(HttpServletRequest , HttpServletResponse , Object )}, + * And clean up Fescar information after servlet method invocation in + * {@link org.springframework.web.servlet.HandlerInterceptor#afterCompletion(HttpServletRequest, HttpServletResponse, Object, Exception)} + */ +public class FescarHandlerInterceptor implements HandlerInterceptor { + + private static final Logger log = LoggerFactory + .getLogger(FescarHandlerInterceptor.class); + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, + Object handler) throws Exception { + + String xid = RootContext.getXID(); + String rpcXid = request.getHeader(RootContext.KEY_XID); + if (log.isDebugEnabled()) { + log.debug("xid in RootContext {} xid in RpcContext {}", xid, rpcXid); + } + + if (xid == null && rpcXid != null) { + RootContext.bind(rpcXid); + if (log.isDebugEnabled()) { + log.debug("bind {} to RootContext", rpcXid); + } + } + return true; + } + + @Override + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, + Object handler, Exception e) throws Exception { + + String rpcXid = request.getHeader(RootContext.KEY_XID); + + if (StringUtils.isEmpty(rpcXid)) { + return; + } + + String unbindXid = RootContext.unbind(); + if (log.isDebugEnabled()) { + log.debug("unbind {} from RootContext", unbindXid); + } + if (!rpcXid.equalsIgnoreCase(unbindXid)) { + log.warn("xid in change during RPC from {} to {}", rpcXid, unbindXid); + if (unbindXid != null) { + RootContext.bind(unbindXid); + log.warn("bind {} back to RootContext", unbindXid); + } + } + } + +} diff --git a/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptorConfiguration.java b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptorConfiguration.java new file mode 100644 index 000000000..c510b1ac7 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/java/org/springframework/cloud/alibaba/fescar/web/FescarHandlerInterceptorConfiguration.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.alibaba.fescar.web; + +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * @author xiaojing + */ +public class FescarHandlerInterceptorConfiguration implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new FescarHandlerInterceptor()).addPathPatterns("/**"); + } +} diff --git a/spring-cloud-alibaba-fescar/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-fescar/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..3560bcee4 --- /dev/null +++ b/spring-cloud-alibaba-fescar/src/main/resources/META-INF/spring.factories @@ -0,0 +1,7 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +org.springframework.cloud.alibaba.fescar.rest.FescarRestTemplateAutoConfiguration,\ +org.springframework.cloud.alibaba.fescar.web.FescarHandlerInterceptorConfiguration,\ +org.springframework.cloud.alibaba.fescar.GlobalTransactionAutoConfiguration,\ +org.springframework.cloud.alibaba.fescar.feign.FescarFeignClientAutoConfiguration,\ +org.springframework.cloud.alibaba.fescar.feign.hystrix.FescarHystrixAutoConfiguration + diff --git a/spring-cloud-starter-alibaba/pom.xml b/spring-cloud-starter-alibaba/pom.xml index 3c91f8d7c..c0de36447 100644 --- a/spring-cloud-starter-alibaba/pom.xml +++ b/spring-cloud-starter-alibaba/pom.xml @@ -16,6 +16,7 @@ spring-cloud-starter-alibaba-nacos-config-server spring-cloud-starter-alibaba-nacos-discovery spring-cloud-starter-alibaba-sentinel + spring-cloud-starter-alibaba-fescar spring-cloud-starter-stream-rocketmq spring-cloud-starter-bus-rocketmq diff --git a/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml new file mode 100644 index 000000000..8376bfc13 --- /dev/null +++ b/spring-cloud-starter-alibaba/spring-cloud-starter-alibaba-fescar/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + + org.springframework.cloud + spring-cloud-starter-alibaba + 0.2.2.BUILD-SNAPSHOT + + spring-cloud-starter-alibaba-fescar + Spring Cloud Starter Alibaba Fescar + + + + org.springframework.cloud + spring-cloud-alibaba-fescar + + + +