基本信息
开发工具 | 版本 |
---|---|
操作系统 | macOS Ventura 13.2.1(Intel) |
IDEA | IntelliJ IDEA 2021.3.1 |
Maven | apache-maven-3.6.3 |
数据库管理工具 | DBeaver 22.3.5 |
Redis GUI | Medis2 |
根据自己使用的IDEA版本,到maven.apache.org/docs/histor… Date**在IDEA版本之前的Maven版本下载,不然会有兼容性问题。
MacOS和Windows系统在后续配置上会有区别,文中已用💻标出请读者注意。
技术选型
微服务框架:SpringCloudAlibaba 2022.0.0.0-RC1 Spring 6.0.6
名称 | SpringCloud | SpringCloudAlibaba |
---|---|---|
注册中心 | Eureka、Consul | Nacos |
配置中心 | SpringCloud Config | Nacos |
网关 | SpringCloud Zuul | SpringCloud Gateway |
负载均衡 | Ribbon | Loadbalancer |
熔断降级 | Hystrix | Sentinel |
服务调用 | Feign | OpenFeign |
SpringCloudAlibaba比SpringCloud和Dubbo具有更好的扩展性,其配套的Nacos也能简化注册中心和配置中心的维护和部署。
注册中心:Nacos 2.2.0.1
配置中心:Nacos 2.2.0.1
Nacos = Spring Cloud Eureka + Spring Cloud Config
Nacos兼具注册中心和配置中心的功能,大大降低开发成本
数据库:MySQL 8.0.32
Nacos需要使用MySQL作为数据存储,MySQL作为当今最流行的数据库,稳定性和扩展性已经得到充分验证。
缓存:Redis7.0.9 + Redisson
客户端使用Redisson,相比于Jedis和Lettuce在分布式系统中的表现,Redisson具有更好的支持,大部分情况下可以采用Jedis + Redisson或Lettuce + Redisson的组合,但是本例为了使开发者的关注点在业务逻辑上,仅使用Redisson。
消息中间件:Apache RocketMQ 5.1.0
目前业界使用的消息中间件有很多,如ActiveMQ、RabbitMQ、RocketMQ、Kafka、ZeroMQ等,还有付费的其他公司维护的中间件,这里我们只对比了互联网公司三大主流消息中间件:RabbitMQ、RocketMQ、Kafka,最终选择在数据量不大,主要功能为业务数据推送,要求可靠性高延时低的业务特点下,我们选择RocketMQ。
数据同步:Canal
搜索引擎【待更新】
集群配置【待更新】
搭建数据库
1. 下载安装MySQL
选择x86架构即可,在低并发的情况下,ARM结构FS模式要比X86性能略强,但在高并发的情况下,MYSQL在X86要比ARM结构表现的更好。
2. 配置my.cnf
默认情况下mysql安装目录中不存在my.cnf文件,需要自己创建,使用以下命令可以查看mysql读取my.cnf的路径
mysql --help|grep 'my.cnf'
输出为:
order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
在mysql目录中创建etc文件夹,并创建my.cnf文件,文件内容如下:
# Example MySQL config file for medium systems.
#
# This is for a system with little memory (32M - 64M) where MySQL plays
# an important part, or systems up to 128M where MySQL is used together with
# other programs (such as a web server)
#
# MySQL programs look for option files in a set of
# locations which depend on the deployment platform.
# You can copy this option file to one of those
# locations. For information about these locations, see:
# http://dev.mysql.com/doc/mysql/en/option-files.html
#
# In this file, you can use all long options that a program supports.
# If you want to know which options a program supports, run the program
# with the "--help" option.
# The following options will be passed to all MySQL clients
[client]
default-character-set=utf8
#password = your_password
port = 3306
socket = /tmp/mysql.sock
# Here follows entries for some specific programs
# The MySQL server
[mysqld]
character-set-server=utf8
init_connect='SET NAMES utf8
port = 3306
socket = /tmp/mysql.sock
skip-external-locking
key_buffer_size = 16M
max_allowed_packet = 1M
table_open_cache = 64
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K
myisam_sort_buffer_size = 8M
character-set-server=utf8
init_connect='SET NAMES utf8'
# Don't listen on a TCP/IP port at all. This can be a security enhancement,
# if all processes that need to connect to mysqld run on the same host.
# All interaction with mysqld must be made via Unix sockets or named pipes.
# Note that using this option without enabling named pipes on Windows
# (via the "enable-named-pipe" option) will render mysqld useless!
#
#skip-networking
# Replication Master Server (default)
# binary logging is required for replication
log-bin=mysql-bin
# binary logging format - mixed recommended
binlog_format=mixed
# required unique id between 1 and 2^32 - 1
# defaults to 1 if master-host is not set
# but will not function as a master if omitted
server-id = 1
# Replication Slave (comment out master section to use this)
#
# To configure this host as a replication slave, you can choose between
# two methods :
#
# 1) Use the CHANGE MASTER TO command (fully described in our manual) -
# the syntax is:
#
# CHANGE MASTER TO MASTER_HOST=<host>, MASTER_PORT=<port>,
# MASTER_USER=<user>, MASTER_PASSWORD=<password> ;
#
# where you replace <host>, <user>, <password> by quoted strings and
# <port> by the master's port number (3306 by default).
#
# Example:
#
# CHANGE MASTER TO MASTER_HOST='125.564.12.1', MASTER_PORT=3306,
# MASTER_USER='joe', MASTER_PASSWORD='secret';
#
# OR
#
# 2) Set the variables below. However, in case you choose this method, then
# start replication for the first time (even unsuccessfully, for example
# if you mistyped the password in master-password and the slave fails to
# connect), the slave will create a master.info file, and any later
# change in this file to the variables' values below will be ignored and
# overridden by the content of the master.info file, unless you shutdown
# the slave server, delete master.info and restart the slaver server.
# For that reason, you may want to leave the lines below untouched
# (commented) and instead use CHANGE MASTER TO (see above)
#
# required unique id between 2 and 2^32 - 1
# (and different from the master)
# defaults to 2 if master-host is set
# but will not function as a slave if omitted
#server-id = 2
#
# The replication master for this slave - required
#master-host = <hostname>
#
# The username the slave will use for authentication when connecting
# to the master - required
#master-user = <username>
#
# The password the slave will authenticate with when connecting to
# the master - required
#master-password = <password>
#
# The port the master is listening on.
# optional - defaults to 3306
#master-port = <port>
#
# binary logging - not required for slaves, but recommended
#log-bin=mysql-bin
# Uncomment the following if you are using InnoDB tables
#innodb_data_home_dir = /usr/local/mysql/data
#innodb_data_file_path = ibdata1:10M:autoextend
#innodb_log_group_home_dir = /usr/local/mysql/data
# You can set .._buffer_pool_size up to 50 - 80 %
# of RAM but beware of setting memory usage too high
#innodb_buffer_pool_size = 16M
#innodb_additional_mem_pool_size = 2M
# Set .._log_file_size to 25 % of buffer pool size
#innodb_log_file_size = 5M
#innodb_log_buffer_size = 8M
#innodb_flush_log_at_trx_commit = 1
#innodb_lock_wait_timeout = 50
[mysqldump]
quick
max_allowed_packet = 16M
[mysql]
no-auto-rehash
# Remove the next comment character if you are not familiar with SQL
#safe-updates
default-character-set=utf8
[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M
[mysqlhotcopy]
interactive-timeout
3. 启动MySQL
# 启动命令
sudo /usr/local/mysql/support-files/mysql.server start
# 查看状态
sudo /usr/local/mysql/support-files/mysql.server status
如果查看状态输出
SUCCESS! MySQL running (28921)
接下来需要配置MySQL允许远程连接
mysql -uroot -p
输入密码
use mysql;
查询host输入:
select user,host from user;
创建host(如果有"%"这个host值,则跳过这一步)
如果没有"%"这个host值,就执行下面这两句:
update user set host='%' where user='root';
flush privileges;
4. DBeaver连接
DBeaver下载地址:dbeaver.io/download/
新建连接选择MySQL,并配置本地服务器地址、端口号、用户名、密码、本地客户端,如果没有驱动点击编辑驱动设置可以自动下载驱动
搭建Nacos
1. 下载Nacos
选择nacos-server-2.2.0.1.tar.gz下载解压即可
2. 配置Nacos
打开conf/application.properties,修改以下内容
#*************** Config Module Related Configurations ***************#
### If use MySQL as datasource:
spring.datasource.platform=mysql
### Count of DB:
db.num=1
### Connect URL of DB:
### 这里需要设置为自己数据库的信息
db.url.0=jdbc:mysql://localhost:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
### 数据库的用户名
db.user.0=root
### 数据库的密码
db.password.0=12345678
### Connection pool configuration: hikariCP
db.pool.config.connectionTimeout=30000
db.pool.config.validationTimeout=10000
db.pool.config.maximumPoolSize=20
db.pool.config.minimumIdle=2
以下配置为鉴权信息,为了安全,建议配置
### If turn on auth system:
nacos.core.auth.enabled=true
### The default token (Base64 String):
nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg
下面首先在DBeaver创建nacos_config数据库,然后进入nacos/conf文件夹,在DBeaver中执行文件夹中的mysql-schema.sql文件,执行成功后数据库结构如下:
3. 启动Nacos
执行命令
sh startup.sh -m standalone
可以使用
tail -f ~/nacos/nacos/logs/start.out
查看启动日志
打印出
2023-03-03 10:00:26,962 INFO Nacos started successfully in stand alone mode. use external storage
说明启动成功
浏览器输入localhost:8848/nacos,可以进入可视化管理页面,账号密码可以从user表查看,默认是nacos/nacos
SpringCloudAlibaba注册Nacos及使用Nacos Config
1. 搭建父子工程
一般情况下项目分为三级:总项目、父工程、子模块
总项目:通用版本管理,统一打包
父工程:微服务最小部署工程,引入所有子模块需要的包
子模块:微服务下模块,用以区分同业务下不同层级
IDEA新建Maven模块如下:tank-forum为总项目、tank-forum-common和tank-forum-user为父工程、tank-forum-user-api为子模块
tank-forum的pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.hanqing</groupId>
<artifactId>tank-forum</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>tank-forum-user</module>
<module>tank-forum-common</module>
</modules>
<!-- 通用版本标签 -->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.boot.version>3.0.3</spring.boot.version>
<spring.cloud.alibaba.version>2022.0.0.0-RC1</spring.cloud.alibaba.version>
<spring.cloud.version>2022.0.1</spring.cloud.version>
<mybatis.plus.version>3.5.3.1</mybatis.plus.version>
<mysql.version>8.0.32</mysql.version>
<jackson.version>2.14.2</jackson.version>
<lombok.version>1.18.26</lombok.version>
</properties>
<!-- 引用依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- spring-boot相关依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring-cloud相关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring-cloud-alibaba相关依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
tank-forum-user的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>tank-forum-user</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<artifactId>tank-forum</artifactId>
<groupId>org.hanqing</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modules>
<module>tank-forum-user-api</module>
</modules>
<dependencies>
<!-- spring-boot-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- spring-boot-jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- nacos 注册发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- nacos 配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
tank-forum-user-api的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<version>1.0-SNAPSHOT</version>
<parent>
<artifactId>tank-forum-user</artifactId>
<groupId>org.hanqing</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>tank-forum-user-api</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.hanqing</groupId>
<artifactId>tank-forum-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
2. yml配置文件
在子模块的resources创建bootstrap.yml
server:
port: 9090
spring:
profiles:
active: dev
application:
name: tank-forum-user
cloud:
nacos:
discovery:
server-addr: localhost:8848
username: nacos
password: nacos
namespace: fc5caf99-7886-4250-bfd2-ee24f44fec0d
group: user
config:
server-addr: localhost:8848
username: nacos
password: nacos
file-extension: yaml
namespace: fc5caf99-7886-4250-bfd2-ee24f44fec0d
group: user
3. Nacos创建命名空间、Config配置
打开Nacos → 命名空间 → 新建命名空间,输入命名空间名,会自动生成命名空间ID,对应的就是yml里的namespace
打开Nacos → 配置管理 → 配置列表,选择命名空间,创建配置,Data ID要严格遵照规则,Nacos服务匹配的时候会按照spring.application.name−{file-extension}.${profiles.active}来匹配文件,如果在dev里没有找到,则会匹配spring.application.name−{file-extension}。
4. 编写启动类和Controller测试类
启动类 TankForumUserApplication.java
package com.hanqing.tank.forum;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class TankForumUserApplication {
public static void main(String[] args) {
SpringApplication.run(TankForumUserApplication.class, args);
}
}
测试Controller UserController.java
package com.hanqing.tank.forum.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RefreshScope
@RequestMapping("/user")
public class UserController {
@Value("${user.name}")
private String name;
@GetMapping("getName")
public String getUserName(){
return "获取到Name:" + name;
}
}
5. 启动测试
启动TankForumUserApplication.java,会打印出Nacos监听的相关信息
打开服务详情,浏览器输入192.168.234.38:9090/user/getName,打印出结果
SpringCloud集成MySQL和Mybatis-Plus
1. 添加yml配置
spring:
datasource:
# mysql8要使用cj.jdbc.Driver
driver-class-name: com.mysql.cj.jdbc.Driver
# 对应数据库地址,注意要加时区
url: jdbc:mysql://localhost/tank_forum?serverTimezone=UTC%2B8&characterEncoding=UTF-8&allowMultiQueries=true
username: root
password: 123456
mybatis-plus:
mapper-locations: classpath:mapper/**/*Mapper.xml
global-config:
db-config:
# 主键类型 0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID";
id-type: assign_id
# 默认数据库表下划线命名
table-underline: true
configuration:
# 返回类型为Map,显示null对应的字段
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
2. 添加数据库表数据
-- tank_forum.forum_user definition
CREATE TABLE `forum_user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3;
INSERT INTO tank_forum.forum_user
(id, name, age)
VALUES(1, '王二', 68);
3. 创建Service、Mapper、Domin结构
建议使用MybatisX自动生成,使用方法参考MybatisX快速开发插件,生成的相关结构如下:
User.java
package com.hanqing.tank.forum.domin;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("forum_user")
public class User {
/** 主键 */
@TableId
private Long userId;
/** 用户名称 */
@TableField("name")
private String userName;
/** 年龄 */
@TableField("age")
private Integer userAge;
}
UserMapper.java
package com.hanqing.tank.forum.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hanqing.tank.forum.domin.User;
import java.util.List;
public interface UserMapper extends BaseMapper<User> {
/** 自定义的查询 */
List<User> selectTestFromUser();
}
UserMapper.xml
<?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.hanqing.tank.forum.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.hanqing.tank.forum.domin.User">
<id property="userId" column="id" jdbcType="BIGINT"/>
<result property="userName" column="name" jdbcType="VARCHAR"/>
<result property="userAge" column="age" jdbcType="INTEGER"/>
</resultMap>
<sql id="Base_Column_List">
id,name,age
</sql>
<select id="selectTestFromUser" resultMap="BaseResultMap">
SELECT
<include refid="Base_Column_List"/>
from forum_user
</select>
</mapper>
UserService.java
package com.hanqing.tank.forum.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hanqing.tank.forum.domin.User;
import java.util.List;
public interface UserService extends IService<User> {
List<User> selectTestData();
}
UserServiceImpl.java
package com.hanqing.tank.forum.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hanqing.tank.forum.domin.User;
import com.hanqing.tank.forum.mapper.UserMapper;
import com.hanqing.tank.forum.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
UserMapper mapper;
@Override
public List<User> selectTestData() {
return mapper.selectTestFromUser();
}
}
改造UserController.java
package com.hanqing.tank.forum.controller;
import com.hanqing.tank.forum.service.UserService;
import com.hanqing.tank.forum.domin.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RefreshScope
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService;
@GetMapping("getName")
public String getUserName() {
List<User> userList = userService.selectTestData();
return "获取到User:" + userList.toString();
}
}
4. 启动测试
重启TankForumUserApplication.java,浏览器输入192.168.234.38:9090/user/getName,打印出结果
部署Redis + Redisson
1. 下载安装Redis
2. 可视化Redis管理工具(💻Mac)
目前Mac系统使用率最高的是Medis2,Windows管理工具很多,可根据喜好下载
下载后解压可直接使用,连接默认本地Redis,可自动生成命令,个人学习使用免费版即可,30美元可升级付费版。
3. 添加Redisson集成SpringBoot依赖
在总工程tank-forum的pom中添加
<redisson.version>3.20.0</redisson.version>
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>
bootstrap.yml里添加
这里其实还有很多配置项,但是为了简单就只配置了最基本的,并且host和port都是默认值
spring:
data:
redis:
host: 127.0.0.1
port: 6379
4. 使用Redisson测试
UserController.java里添加RedissonClient和测试方法:
@Autowired
RedissonClient redissonClient;
@GetMapping("testRedisson")
public String testRedisson() {
// 此处一定要指定 new StringCodec()编码格式,不然Medis会有乱码的问题
RBucket<Object> bucket = redissonClient.getBucket("USER_CACHE", new StringCodec());
bucket.set("我是缓存的用户数据");
return "存放用户缓存:" + bucket.get();
}
浏览器输入192.168.234.38:9090/user/testRedisson,并在Medis2里可以看到存放的数据
一般情况下,我们不会直接操作RedissonClient,而是在外直接封装一个RedisUtil来操作,并实现一些常用的Redis底层的功能,但是这相当于把Redisson对Redis的封装又拆开了。这也是为什么实际开发中一般会采用Jedis + Redisson的原因。所以实际开发中,建议Jedis进行Redis基础功能的操作,Redisson专注分布式环境下的一些操作,如各种锁等。
另外关于Redisson编码的问题,自3.19.0之后,Redisson默认编解码器变更为了org.redisson.codec.Kryo5Codec,编码格式不是UTF-8,这导致一些没有内置此编码器的可视化Redis工具看到的数据乱码。一般情况下我们不需要修改默认的编码格式,也是为了安全和稳定着想,本例中强制指定了用org.redisson.client.codec.StringCodec解码器,默认UTF-8存储,其实是不安全的一种做法,只是为了方便用Medis展示数据而已。
部署RocektMQ
1. 启动NameServer
下载地址:rocketmq.apache.org/zh/download 在无二次开发需求的情况下,直接下载Binary二进制文件即可。
解压后,找到解压包中bin目录下的mqnamesrv,双击启动,或使用命令行启动,显示The Name Server boot success.说明启动成功。
2. 启动Broker+Proxy
官网建议5.x版本的RocketMQ采用Broker和Proxy同进程的部署方式,所以这里我们使用命令行启动,可以指定启动方式。
如图,在bin目录输入命令:
sh mqbroker -n localhost:9876 --enable-proxy &
可以在~/logs/rocketmqlogs/proxy.log中查询日志,日过看到“The broker[brokerName,ip:port] boot success..”,这表明 broker 已成功启动。
3. RocketMQ可视化
RocketMQ的可视化工具原名rocketmq-console,后改名为 RocketMQ Dashboard 本质上是一个SpringBoot项目,配置好对应的RocketMQ之后启动即可使用。
下载地址:github.com/apache/rock… 将项目下载后在IDEA中打开,配置RocketMQ对应的地址和端口(默认都是127.0.0.1:9876),同时修改server:port:8082
运行App.java,启动成功后在浏览器输入127.0.0.1:8082,即可打开RocketMQ仪表盘,右上角可以转换成中文。
启动方式后续可以直接使用 java -jar,这里不做赘述,具体可以参考其中的README.md
4. 创建Topic
可以使用mqadmin命令创建,因为这里我们搭建了RocketMQ仪表盘,所以直接使用界面创建。
如果正常启动了NameServer、Broker+Proxy,默认集群名和BROKER名如图所示。这里我们创建一个用来进行用户修改密码通知的Topic。
点击提交,创建成功。
5. JavaSDK测试RocketMQ消息收发
5.1 引入jar包
在tank-forum的pom.xml里引入
<rocketmq.version>5.0.4</rocketmq.version>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client-java</artifactId>
<version>${rocketmq.version}</version>
</dependency>
tank-forum-common的pom.xml里引入
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client-java</artifactId>
</dependency>
5.2 编写Producer生产者
在tank-forum-common中新建RocketMQUtil.java
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.apache.rocketmq.client.apis.ClientConfiguration;
import org.apache.rocketmq.client.apis.ClientConfigurationBuilder;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.message.Message;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.SendReceipt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RocketMQUtil {
private static final Logger logger = LoggerFactory.getLogger(RocketMQUtil.class);
public static void sendSimpleMsg(JSONObject msgJson, String topic) throws ClientException {
// 接入点地址,需要设置成Proxy的地址和端口列表,一般是xxx:8081;xxx:8081。
String endpoint = "127.0.0.1:8081";
// 消息发送的目标Topic名称,需要提前创建。
ClientServiceProvider provider = ClientServiceProvider.loadService();
ClientConfigurationBuilder builder = ClientConfiguration.newBuilder().setEndpoints(endpoint);
ClientConfiguration configuration = builder.build();
// 初始化Producer时需要设置通信配置以及预绑定的Topic。
Producer producer = provider.newProducerBuilder()
.setTopics(topic)
.setClientConfiguration(configuration)
.build();
// 普通消息发送。
Message message = provider.newMessageBuilder()
.setTopic(topic)
// 设置消息索引键,可根据关键字精确查找某条消息。
.setKeys("messageKey")
// 设置消息Tag,用于消费端根据指定Tag过滤消息。
.setTag("messageTag")
// 消息体。
.setBody(JSON.toJSONBytes(msgJson))
.build();
try {
// 发送消息,需要关注发送结果,并捕获失败等异常。
SendReceipt sendReceipt = producer.send(message);
logger.info("Send message successfully, messageId={}", sendReceipt.getMessageId());
} catch (ClientException e) {
logger.error("Failed to send message", e);
}
// producer.close();
}
}
在tank-forum-user-api中的UserController中新增方法
@GetMapping("resetPassword")
public String resetPassword() throws ClientException {
String topic = "user_reset_password_topic";
JSONObject json = new JSONObject();
json.put("password", "123");
RocketMQUtil.sendSimpleMsg(json, topic);
return "修改密码成功";
}
浏览器访问192.168.12.38:9090/user/resetPassword,可以看到控制台打出发送成功的消息
同时我们打开RocketMQ仪表盘,点开消息一列,可以看到我们发送的消息,点开详情可以看到具体内容。
注:Apache RocketMQ 服务端5.x版本开始,生产者是匿名的,无需管理生产者分组(ProducerGroup);对于历史版本服务端3.x和4.x版本,已经使用的生产者分组可以废弃无需再设置,且不会对当前业务产生影响。
5.3 编写Consumer消费者
Apache RocketMQ 支持SimpleConsumer和PushConsumer两种消费者类型,SimpleConsumer属于主动拉取,适用于自定义业务流程,PushConsumer为回调推送,适用于被动通知,我们这里采用PushConsumer的方式进行消费,具体区别请参考官方文档:rocketmq.apache.org/zh/docs/fea…
首先要创建ConsumerGroup,用于管理消费组,如图创建名为user_reset_password_group的消费组。
然后在tank-forum-user中新建tank-forum-user-consumer子模块,引入jar包,用于用户相关消息的消费,编写UserConsumer.java
import org.apache.rocketmq.client.apis.ClientConfiguration;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.consumer.ConsumeResult;
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
import org.apache.rocketmq.client.apis.consumer.FilterExpressionType;
import org.apache.rocketmq.client.apis.consumer.PushConsumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
public class UserConsumer {
private static final Logger logger = LoggerFactory.getLogger(UserConsumer.class);
private UserConsumer() {
}
public static void main(String[] args) throws ClientException, IOException, InterruptedException {
final ClientServiceProvider provider = ClientServiceProvider.loadService();
// 接入点地址,需要设置成Proxy的地址和端口列表,一般是xxx:8081;xxx:8081。
String endpoints = "127.0.0.1:8081";
ClientConfiguration clientConfiguration = ClientConfiguration.newBuilder()
.setEndpoints(endpoints)
.build();
// 订阅消息的过滤规则,表示订阅所有Tag的消息。
String tag = "*";
FilterExpression filterExpression = new FilterExpression(tag, FilterExpressionType.TAG);
// 为消费者指定所属的消费者分组,Group需要提前创建。
String consumerGroup = "user_reset_password_group";
// 指定需要订阅哪个目标Topic,Topic需要提前创建。
String topic = "user_reset_password_topic";
// 初始化PushConsumer,需要绑定消费者分组ConsumerGroup、通信参数以及订阅关系。
PushConsumer pushConsumer = provider.newPushConsumerBuilder()
.setClientConfiguration(clientConfiguration)
// 设置消费者分组。
.setConsumerGroup(consumerGroup)
// 设置预绑定的订阅关系。
.setSubscriptionExpressions(Collections.singletonMap(topic, filterExpression))
// 设置消费监听器。
.setMessageListener(messageView -> {
// 处理消息并返回消费结果。
logger.info("Consume message successfully, messageId={}", messageView.getMessageId());
String str = StandardCharsets.UTF_8.decode(messageView.getBody()).toString();
logger.info("读取到的消息内容:{}", str);
return ConsumeResult.SUCCESS;
})
.build();
Thread.sleep(Long.MAX_VALUE);
// 如果不需要再使用 PushConsumer,可关闭该实例。
// pushConsumer.close();
}
}
启动main方法进行监听,可以看到控制台打出之前发送的消息