spriongcloud

Spring Cloud

SpringCloud

Eureka

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!--父工程-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--微服务工程-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>3.1.3</version>
</dependency>
1
2
3
4
5
6
7
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
1
2
3
4
5
6
7
# eureka微服务
eureka:
client:
fetch-registry: false
register-with-eureka: false
service-url:
defaultZone: http://localhost:自定义端口/eureka/
1
2
3
4
5
# 其他需要使用eureka服务的服务
eureka:
client:
service-url:
defaultZone: http://localhost:自定义端口/eureka/·

访问地址:http://localhost:自定义端口

1
2
3
4
5
6
7
8
9
10
11
12
// 需要使用eureka服务的服务
@Configuration
public class BeanConfiguration {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
// http:// + spring.application.servername + /controller请求
// 不需要写ip+端口号了
restTemplate.getForObject("http://user-service/user/", 自定义.class);

LoadBalancer

1
2
3
4
5
6
7
8
9
10

@Configuration
public class LoadBalancerConfig {
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory){
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name);
}
}
1
2
3
4
5
6
7
8
9
@Configuration
@LoadBalancerClient(value = "user-service",configuration = LoadBalancerConfig.class)
public class BeanConfiguration {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}

OpenFeign

1
2
3
4
5
6
7
8
@SpringBootApplication
@MapperScan("com.kuang.mapper")
@EnableFeignClients
public class BorrowApplication {
public static void main(String[] args) {
SpringApplication.run(BorrowApplication.class,args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
@FeignClient("user-service")
public interface UserClient {

@RequestMapping("/user/{uid}")
User getUserByid(@PathVariable("uid") Integer uid);
}
@FeignClient("book-service")
public interface BookServier {
@RequestMapping("/book/{bid}")
Book getBookByid(@PathVariable("bid") Integer bid);
}

1
2
3
4
5
6
7
8
9
@Autowired
BookServier bookServier;
@Autowired
UserClient userClient;

// 原先的调用方式
// User user = restTemplate.getForObject("http://localhost:8300/user/" + uid, User.class);
// 可以改为
User user = userClient.getBookByid(uid);

Hystrix 服务熔断

Hystrix

1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
1
2
3
4
5
6
7
@EnableHystrix
public class BorrowApplication {
public static void main(String[] args) {
SpringApplication.run(BorrowApplication.class,args);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

@RestController
public class BorrowController {
@Autowired
BorrowService borrowService;
@HystrixCommand(fallbackMethod = "onError")
@RequestMapping("/borrow/{uid}")
public BorrowDetail getBorrowDetailByUid(@PathVariable("uid") Integer uid){
return borrowService.getBorrowDetailByUid(uid);
}

BorrowDetail onError(int uid){
return new BorrowDetail(null, Collections.emptyList());
}
}

OpenFeign

1
2
3
feign:
circuitbreaker:
enabled: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@FeignClient(value = "user-service",fallback = UserFallbackClient.class)
public interface UserClient {

@RequestMapping("/user/{uid}")
User getUserByid(@PathVariable("uid") Integer uid);
}



@Component
public class UserFallbackClient implements UserClient {
@Override
public User getUserByid(Integer uid) {
return new User(0,0,"error","error");
}
}

监控

新建工程

1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
1
2
3
4
5
server:
port: 8888
hystrix:
dashboard:
proxy-stream-allow-list: "localhost"
1
2
3
4
5
6
7
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class,args);
}
}

在要监控的工程中

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1
2
3
4
5
management:
endpoints:
web:
exposure:
include: '*'

Hystrix Dashboard访问地址 http://localhost:port/hystrix

监控地址 http://localhost:服务端口/actuator/hystrix.stream

Gateway

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
server:
port: 9999

eureka:
client:
service-url:
defaultZone: http://localhost:8400/eureka/
spring:
application:
name: gateway

路由过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Component
public class MyFilter implements GlobalFilter.Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// 判断是否包含某个参数
List<String> value = request.getQueryParams().get("key");
// 进行一些逻辑的判断
if (value!=null && value.contains("value")){
return chain.filter(exchange);
}else {
return exchange.getResponse().setComplete();
}

}
@Override
public int getOreder(){
return 0;
}
}

Config

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
eureka:
client:
service-url:
defaultZone: http://localhost:8400/eureka/
server:
port: 1010
spring:
application:
name: config-service
cloud:
config:
server:
git:
uri: # git地址
default-label: main
1
2
3
4
5
6
7
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class,args);
}
}

http://ip:port/{git分支}/{服务名称}-{环境}.yml

http://ip:port/{服务名称}/{环境}/{git分支}

bootstrap.yaml

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
1
2
3
4
5
6
7
spring:
cloud:
config:
name: #远程想要获取的文件名称
uri: #配置服务器 http://ip:port
profile: #环境
label: #分支

SpringCloud Alibaba

nacos

dependencyManagement

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.0.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>

子工程

1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
1
2
3
4
5
6
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
1
2
3
4
5
6
@FeignClient("book-service")
public interface BookClient {

@RequestMapping("/book/{bid}")
Book getBookByid(@PathVariable("bid") Integer bid);
}
1
2
3
4
5
@FeignClient("user-service")
public interface UserClient {
@RequestMapping("/user/{uid}")
User getUserByid(@PathVariable("uid") Integer uid);
}

常驻服务

1
2
3
4
5
spring:
cloud:
nacos:
discovery:
ephemeral: false #改为常驻服务

集群分区

1
2
3
4
5
spring:
cloud:
nacos:
discovery:
cluster-name: name #改为常驻服务

LoadBalance

1
2
3
4
5
spring:
cloud:
loadbalancer:
nacos:
enable: true

Config

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

bootstrap.yaml

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: user-service #与云端配置文件保持一致
profiles:
active: dev #与云端配置文件保持一致
cloud:
nacos:
config:
file-extension: yaml
server-addr: localhost:8848

@RefreshScope配置文件热更新

Sentinel

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
1
2
3
4
5
spring:
cloud:
sentinel:
transport:
dashboard: localhost:8858

流量控制

@SentinelResource(value=自定义名称)

1
2
3
4
spring:
cloud:
sentinel:
web-context-unify: false
1
2
3
4
5
6
7
8
@RequestMapping("/blocked")
JSONObject blocked(){
JSONObject object = new JSONObject();
object.put("code",403);
object.put("success",false);
object.put("message","你的请求过快,请稍后再试!");
return object;
}
1
2
3
4
spring:
cloud:
sentinel:
block-page: /blocked
1
2
3
4
5
6
7
8
9
@SentinelResource(value=自定义名称,blockHandle="blocked")
//在需要限流的方法前添加注解
public BorrowDetail getBorrowDetailByUid(Integer uid) {
return new BorrowDetail(user,books);
}
//替代方案,返回值要保持一致,参数还要加一个BlockException
public BorrowDetail blocked(Integer uid,lockException e) {
return new BorrowDetail(null,Collections.emptyList());
}
1
2
3
feign:
sentinel:
enable: true
1
2
3
4
5
6
@FeignClient(value = "user-service",fallback = UserClientImpl.class)
public interface UserClient {
@RequestMapping("/user/{uid}")
User getUserByid(@PathVariable("uid") Integer uid);
}

1
2
3
4
5
6
7
8
9
@Component
public class UserClientImpl implements UserClient {
@Override
public User getUserByid(Integer uid) {
User user = new User();
user.setName("我是替代方案");
return user;
}
}

Seata

1
2
3
4
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
1
2
3
4
5
6
7
8
seata:
service:
vgroup-mapping:
# 这里需要对事务组做映射,默认的分组名称为 应用名称-seata-service-group,将其映射到default集群
# 很关键,否则会找不到服务
book-service-seata-service-group: default
grouplist:
default: localhost:8868

每个服务的启动类上添加@EnableAutoDataSourceProxy

在需要支持事务的方法上添加@GlobalTransactional

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
create table `undo_log`
(
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`branch_id` BIGINT(20) NOT NULL,
`xid` VARCHAR(100) NOT NULL,
`context` VARCHAR(128) 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`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
)ENGINE = InnoDB
AUTO_INCREMENT =1
DEFAULT CHARSET utf8;

中间件

OAuth2

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1
2
3
4
5
spring:
session:
store-type: redis
redis:
host: 180.76.100.158

验证服务器

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
1
2
3
4
server:
port: 8500
servlet:
context-path: /sso
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.kuang.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@SneakyThrows
@Bean
@Override
public UserDetailsService userDetailsService(){
return super.userDetailsServiceBean();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
auth
.inMemoryAuthentication()
.passwordEncoder(encoder)
.withUser("test").password(encoder.encode("123456")).roles("USER");
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.kuang.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;

import javax.annotation.Resource;

@EnableAuthorizationServer
@Configuration
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {

@Resource
private AuthenticationManager manager;

@Resource
UserDetailsService userDetailsService;

private final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.passwordEncoder(encoder)
.allowFormAuthenticationForClients()
.checkTokenAccess("permitAll()");
}

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("web")
.secret(encoder.encode("654321"))
.autoApprove(false)
.scopes("book","user","borrow")
.redirectUris("http://localhost:8100/login")
.authorizedGrantTypes("client_credentials","password","implicit","authorization_code","refresh_token");
}

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.userDetailsService(userDetailsService)
.authenticationManager(manager);
}
}

基于@EnableOAthu2Sso实现
1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
security:
oauth2:
client:
client-id: web
client-secret: 654321
#Token获取地址
access-token-uri: http://localhost:8500/sso/oauth/token
#验证页面地址
user-authorization-uri: http://localhost:8500/sso/oauth/authorize
resource:
#Token信息获取和校验地址
token-info-uri: http://localhost:8500/sso/oauth/check_token
基于@EnableResourceServer实现
1
2
3
4
5
6
7
8
security:
oauth2:
client:
client-id: web
client-secret: 654321
resource:
#Token信息获取和校验地址
token-info-uri: http://localhost:8500/sso/oauth/check_token
1
2
3
4
5
6
7
8
9
@Configuration
public class ResourceConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().access("#oauth2.hasScope('book')");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13

@Configuration
public class WebConfiguration {

@Resource
OAuth2ClientContext context;

@Bean
public OAuth2RestTemplate restTemplate(){
return new OAuth2RestTemplate(new ClientCredentialsResourceDetails(),context);
}

}
OpenFeign 整合 OAuth2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";

private static final String BEARER_TOKEN_TYPE = "Bearer";

private final OAuth2RestTemplate oAuth2RestTemplate;

public OAuth2FeignRequestInterceptor(OAuth2RestTemplate oAuth2RestTemplate) {
this.oAuth2RestTemplate = oAuth2RestTemplate;
}

@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header(AUTHORIZATION_HEADER,
String.format("%s %s",
BEARER_TOKEN_TYPE,
oAuth2RestTemplate.getAccessToken().toString()));
}

}
JWT

验证服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
//追加注入两个类
@Bean
public JwtAccessTokenConverter tokenConverter(){
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("kuang");
return converter;
}
@Bean
public TokenStore tokenStore(JwtAccessTokenConverter converter){
return new JwtTokenStore(converter);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@EnableAuthorizationServer
@Configuration
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {

@Resource
private AuthenticationManager manager;

@Resource
UserDetailsService userDetailsService;

@Resource
TokenStore store;

@Resource
JwtAccessTokenConverter converter;

private AuthorizationServerTokenServices serverTokenServices(){
DefaultTokenServices services = new DefaultTokenServices();
services.setSupportRefreshToken(true);
services.setTokenStore(store);
services.setTokenEnhancer(converter);
return services;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenServices(serverTokenServices())
.userDetailsService(userDetailsService)
.authenticationManager(manager);
}
}

资源服务器

1
2
3
4
5
security:
oauth2:
resource:
jwt:
key-value: kuang

Redis

主从复制

老版本slaveof ip port

新版本replicaof ip port

解除slaveof no one或者replicaof no one

哨兵模式

创建一个redis服务

配置文件只保留sentinel monitor 名称 ip port 1

启动时添加参数redis-servier --sentinel

ShardingJDVBC

分库
1
2
3
4
5
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.0</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
spring:
shardingsphere:
datasource:
names: db0,db1
db0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3306/shardingstudy
username: root
password: Kuang001102
db1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3305/shardingstudy
username: root
password: Kuang001102
mybatis:
mapper-locations: classpath:mapper/*.xml
1
2
3
4
5
6
7
8
@Data
@AllArgsConstructor
@NonDeterministic
public class User {
int id;
String name;
String password;
}
1
2
3
4
5
6
public interface UserMapper {

User getUserById(int id);

int addUser(User user);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.kuang.mapper.UserMapper">

<select id="getUserById" resultType="com.kuang.entity.User">
select * from user where id = #{id}
</select>

<insert id="addUser" parameterType="com.kuang.entity.User">
insert into user(id,name,password) values (#{id},#{name},#{password})
</insert>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
spring:
shardingsphere:
datasource:
names: db0,db1
db0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3306/shardingstudy
username: root
password: Kuang001102
db1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3305/shardingstudy
username: root
password: Kuang001102
rules:
sharding:
tables:
#这里填写表的名称
test:
#填写实际的路由节点
#简写 db$->{0..1].test
actual-data-nodes: db0.user,db1.user
#策略
database-strategy:
#使用标准配置
standard:
#参与分片运算的字段
sharding-column: id
#自定义算法名称
sharding-algorithm-name: my-alg
sharding-algorithms:
#自定义算法名称
my-alg:
#算法名称,官方提供了很多种
type: MOD
props:
sharding-count: 2
props:
#开启日志,方便观察
sql-show: true
分表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
spring:
shardingsphere:
datasource:
names: db0,db1
db0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3306/shardingstudy
username: root
password: Kuang001102
db1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3305/shardingstudy
username: root
password: Kuang001102
rules:
sharding:
tables:
#这里填写表的名称,逻辑表
user:
#填写实际的路由节点
#简写 db$->{0..1].test
actual-data-nodes: db0.user,db1.user
#策略
table-strategy:
#使用标准配置
standard:
#参与分片运算的字段
sharding-column: id
#自定义算法名称
sharding-algorithm-name: my-alg
sharding-algorithms:
#自定义算法名称
my-alg:
#算法名称,官方提供了很多种
type: INLINE
props:
algorithm-expression: test_$->{id % 2}
allow-range-query-with-inline-sharding: false
props:
#开启日志,方便观察
sql-show: true
分布式序列算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
spring:
shardingsphere:
datasource:
names: db0,db1
db0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3306/shardingstudy
username: root
password: Kuang001102
db1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3305/shardingstudy
username: root
password: Kuang001102
rules:
sharding:
tables:
#这里填写表的名称,逻辑表
user:
#填写实际的路由节点
#简写 db$->{0..1].test
actual-data-nodes: db0.user,db1.user
#策略
database-strategy:
#使用标准配置
standard:
#参与分片运算的字段
sharding-column: id
#自定义算法名称
sharding-algorithm-name: my-alg
#主键生成策略
key-generate-strategy:
column: id
key-generate-name: my-gen
key-generators:
my-gen:
type: SNOWFLAKE
props:
worder-id: 666
sharding-algorithms:
#自定义算法名称
my-alg:
#算法名称,官方提供了很多种
type: MOD
props:
sharding-count: 2
props:
#开启日志,方便观察
sql-show: true

主键类型为long,int长度不够

读写分离

前提是配置了主从模式,从机并且配置了只读模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
spring:
shardingsphere:
datasource:
names: db0,db1
db0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3306/shardingstudy
username: root
password: Kuang001102
db1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://180.76.100.158:3305/shardingstudy
username: root
password: Kuang001102
rules:
readwrite-splitting:
data-sources:
#自定义名称
user-db:
#使用静态类型,动态Dynamic类型可以自动发现auto-aware-data-source-name
type: Static
props:
#写库只能配置一个
write-data-source-name: db0
#读库可以配置多个
read-data-source-names: db1
#负载均衡策略
load-balancer-name: my-load
load-balancers:
my-load:
type: ROUND_ROBIN
sharding:
tables:
#这里填写表的名称,逻辑表
user:
#填写实际的路由节点
#简写 db$->{0..1].test
actual-data-nodes: db0.user,db1.user
#策略
database-strategy:
#使用标准配置
standard:
#参与分片运算的字段
sharding-column: id
#自定义算法名称
sharding-algorithm-name: my-alg
#主键生成策略
key-generate-strategy:
column: id
key-generate-name: my-gen
key-generators:
my-gen:
type: SNOWFLAKE
props:
worder-id: 666
sharding-algorithms:
#自定义算法名称
my-alg:
#算法名称,官方提供了很多种
type: MOD
props:
sharding-count: 2
props:
#开启日志,方便观察
sql-show: true

RabbitMQ

docker安装rabbitmq

1
2
3
4
5
6
7
docker pull rabbitmq:management
docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq --restart=always --hostname myRabbit rabbitmq:management
docker exec -it rabbitmq
rabbitmq-plugins enable rabbitmq_management
rabbitmqctl add_user admin Kuang001102
rabbitmqctl set_user_tags admin administrator
rabbitmqctl set_permissions -p "/" username ".*" ".*" ".*"
1
2
3
4
5
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.14.2</version>
</dependency>

基本步骤

创建连接工厂
1
2
3
4
5
6
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("180.76.100.158");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("Kuang001102");
factory.setVirtualHost("/");
创建连接
1
2
//创建连接
connection = factory.newConnection("生产者");
创建连接通道
1
2
//通过连接获取通道
channel = connection.createChannel();
创建交换机
1
2
3
4
5
6
7
8
9
10
11
//准备交换机
String exchangeName = "fanout-exchange";
String type = "fanout";
/*
@params1 exchange:交换机名称
@params2 type:官网指定的交换机类型,不能自定义
@params3 持久化:是否持久化
@params4 自动删除:是否自动删除
@params5 额外参数
*/
channel.exchangeDeclare(exchangeName, type, false, false, null);
创建消息队列
1
2
3
4
5
6
7
8
9
/*
@params1 队列的名称
@params2 是否要持久化 durable=false
@params3 排他性
@params4 是否自动删除
@params5 携带一些附加参数
*/
String queueName = "queue1";
channel.queueDeclare(queueName,false,false,false,null);
将交换机和消息队列绑定
1
2
3
String routeKey = "";
//将交换器与队列通过路由键绑定
channel.queueBind(queueName, exchangeName, routeKey);
发送消息
1
2
3
4
5
6
7
8
9
10
11
12
//准备消息内容
String message = "hello world";
//发送消息给队列
//发送消息给队列
/*
@params1 exchange:指定交换机,参数为空则为默认交换机
@params2 消息队列
@params3 路由key
@params4 BasicProperties props
@params5 字节数组类型的消息
*/
channel.basicPublish(exchangeName,queueName,routeKey,null,message.getBytes());
关闭连接
1
2
channel.close();
connection.close();

简单模式

生产者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class Producer {
public static void main(String[] args) {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("180.76.100.158");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("Kuang001102");
factory.setVirtualHost("/");
Connection connection =null;
Channel channel = null;
//创建连接
try {
connection = factory.newConnection("生产者");
//通过连接获取通道
channel = connection.createChannel();
//通过通道创建交换机,声明队列,绑定关系,路由key,发送消息和接收消息
/*
@params1 队列的名称
@params2 是否要持久化 durable=false
@params3 排他性
@params4 是否自动删除
@params5 携带一些附加参数
*/
String queueName = "queue1";
channel.queueDeclare(queueName,false,false,false,null);
//准备消息内容
String message = "hello world";
//发送消息给队列
channel.basicPublish("",queueName,null,message.getBytes());
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭通道
if (channel != null && channel.isOpen()){
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//关闭连接
if (connection != null && connection.isOpen()){
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}


}
}

消费者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class Consumer {
public static void main(String[] args) {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("180.76.100.158");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("Kuang001102");
factory.setVirtualHost("/");
Connection connection =null;
Channel channel = null;
//创建连接
try {
connection = factory.newConnection("生产者");
//通过连接获取通道
channel = connection.createChannel();
//获取信息
channel.basicConsume("queue1", true, new DeliverCallback() {
@Override
public void handle(java.lang.String s, Delivery delivery) throws IOException {
System.out.println(delivery.getBody());
}
}, new CancelCallback() {
@Override
public void handle(java.lang.String s) throws IOException {
System.out.println("接收失败");
}
});
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭通道
if (channel != null && channel.isOpen()){
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//关闭连接
if (connection != null && connection.isOpen()){
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

Fanout模式

自定义交换机

自定义消息队列

交换机绑定消息队列

生产者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.kuang.routing;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class Producer {
public static void main(String[] args) {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("180.76.100.158");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("Kuang001102");
factory.setVirtualHost("/");
Connection connection =null;
Channel channel = null;
try {
//创建连接
connection = factory.newConnection("生产者");
//通过连接获取通道
channel = connection.createChannel();
//准备消息内容
String message = "hello world";
//准备交换机
//确保rabbitmq中存在此交换机
String exchangeName = "fanout-exchange";
String type = "fanout";
//定义路由key
String routeKey = "";
//发送消息给队列
channel.basicPublish(exchangeName,routeKey,null,message.getBytes());
System.out.println("发送消息成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("发送消息失败");
} finally {
//关闭通道
if (channel != null && channel.isOpen()){
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//关闭连接
if (connection != null && connection.isOpen()){
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}


}
}

消费者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
package com.kuang.routing;

import com.rabbitmq.client.*;


import java.io.IOException;

public class Consumer {
public static Runnable runnable = new Runnable() {
@Override
public void run() {
//创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("180.76.100.158");
factory.setPort(5672);
factory.setUsername("admin");
factory.setPassword("Kuang001102");
factory.setVirtualHost("/");
//获取队列名称
final java.lang.String queueName = Thread.currentThread().getName();
Connection connection =null;
Channel channel = null;
//创建连接
try {
connection = factory.newConnection("生产者");
//通过连接获取通道
channel = connection.createChannel();

//获取信息
channel.basicConsume(queueName, true, new DeliverCallback() {
@Override
public void handle(java.lang.String s, Delivery delivery) throws IOException {
System.out.println(queueName+"=======>"+new String(delivery.getBody(),"UTF-8"));
System.in.read();
}
}, new CancelCallback() {
@Override
public void handle(java.lang.String s) throws IOException {
System.out.println("接收失败");
}
});
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭通道
if (channel != null && channel.isOpen()){
try {
channel.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//关闭连接
if (connection != null && connection.isOpen()){
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
};


public static void main(String[] args) {
new Thread(runnable,"queue1").start();
new Thread(runnable,"queue2").start();
new Thread(runnable,"queue3").start();
}
}

direct模式

1
2
3
4
5
6
7
//准备交换机
//确保rabbitmq中存在此交换机
String exchangeName = "direct-exchange";
//定义路由key
String routeKey = "wechat";
//发送消息给队列
channel.basicPublish(exchangeName,routeKey,null,message.getBytes());

topic模式

1
2
3
4
5
6
7
8
9
// # : 代表匹配一级或者多级
// * : 至少匹配一级
//准备交换机
//确保rabbitmq中存在此交换机
String exchangeName = "topic-exchange";
//定义路由key
String routeKey = "com.email.wechat";
//发送消息给队列
channel.basicPublish(exchangeName,routeKey,null,message.getBytes());

Springboot整合rabbitmq

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
server:
port: 8080

spring:
rabbitmq:
username: admin
password: Kuang001102
virtual-host: /
host: 180.76.100.158:22
port: 5672
生产者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Service
public class OrderService {
@Resource
private RabbitTemplate rabbitTemplate;
/**
* @description: 模拟用户下单
* @param userId:
* @param productId:
* @param num:
* @return void
* @author: Kuang
* @date: 2022/7/24 15:58
*/


public void makeOrder(String userId,String productId,int num){
// 1 :根据商品id查询库存是否充足
// 2 :保存订单
String orderId = UUID.randomUUID().toString();
System.out.println("订单生成成功");
// 3 : 通过mq来完成消息的分发
// 参数1:交换机 参数2:路由key/queue队列名称 参数3:消息内容
String exchangeName = "fanout_order_exchange";
String routingKey = "";
rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Configuration
public class RabbitMQConfiguration {
// 1:声明注册fanout模式的交换机
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanout_order_exchange",true,false);
}
// 2:声明队列 sms.fanout.queue email.fanout.queue wechat.fanout.queue
@Bean
public Queue smsQueue(){
return new Queue("sms.fanout.queue",true);
}
@Bean
public Queue emailQueue(){
return new Queue("email.fanout.queue",true);
}
@Bean
public Queue wechatQueue(){
return new Queue("wechat.fanout.queue",true);
}
// 3:完成绑定关系(队列和交换机完成绑定关系)
@Bean
public Binding smsBingding(){
return BindingBuilder.bind(smsQueue()).to(fanoutExchange());
}
@Bean
public Binding emailBingding(){
return BindingBuilder.bind(emailQueue()).to(fanoutExchange());
}
@Bean
public Binding wechatBingding(){
return BindingBuilder.bind(wechatQueue()).to(fanoutExchange());
}
}

1
orderService.makeOrder("1","1",12);
消费者
1
2
3
4
5
6
7
8
@RabbitListener(queues = {"email.fanout.queue"})
@Service
public class EmailConsumer {
@RabbitHandler
public void receiveMessage(String message){
System.out.println("email========>"+message);
}
}