Springboot+Redis+dubbo+zookeeper
一、Springboot+Redis整合
- 导入依赖(直接导入nosql中的redis)
<!--操作redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 配置连接
# 本地redis测试
spring.redis.host=127.0.0.1
spring.redis.port=6379
#新版本使用lettuce(线程安全)
spring.redis.lettuce.pool.max-active=8
#jedis已近失效---不使用
spring.redis.jedis.pool.max-active=8
- 测试
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
// opsForValue操作字符串
redisTemplate.opsForValue().set("lsx","carters123456");
System.out.println(redisTemplate.opsForValue().get("lsx"));
}
- 实际开发中自己定义RedisTemplate
添加依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
实体类
@Component // 变成组件
@AllArgsConstructor// 有参数
@NoArgsConstructor// 无参数
@Data
// 企业中需要序列化
public class User implements Serializable {
private String name;
private int age;
}
操作
@Test
void test() {
// 真实项目中
User user = new User("廖述幸", 3);
// 对象序列化
String jsonUser = JSONObject.toJSONString(user);
redisTemplate.opsForValue().set("user",jsonUser);
System.out.println(redisTemplate.opsForValue().get("user"));
}
输出结果:{“age”:3,“name”:“廖述幸”}
- 自定义Redistemplate
添加依赖
<!--ObjectMapper的依赖-->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
配置RedistemplateConfig
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.net.UnknownHostException;
@Configuration
public class RedisConfig {
// 企业中固定模板
// 编写我们自己的 redisTemplate
// <bean id=方法名:redisTemplate class=返回值:RedisTemplate
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory)
throws UnknownHostException {
// 1、默认设置,为了开发方便使用String + Object
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 2、连接工厂
template.setConnectionFactory(factory);
// 3、json序列化的配置
// jackson解析任意对象
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 4、String的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key统一采用String填写
template.setKeySerializer(stringRedisSerializer);
// hash的key也用String填写
template.setHashKeySerializer(stringRedisSerializer);
// value的序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式也采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
使用自己的RedisTemplate
@Autowired
@Qualifier("redisTemplate")// 使用id进行定义,防止未生效
private RedisTemplate redisTemplate;
@Test
void test() {
// 真实项目中 (自己定义的redisTemplate)
User user = new User("廖述幸", 3);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
输出结果:User(name=廖述幸, age=3)
- 与未定义时候的区别
二、Springboot+bubbo整合
分布式:一个人只用一台机,为啥考虑多台机?
RPC:通信 和 序列化 核心模块
- 下载 dubbo-admin后台管理
https://github.com/apache/dubbo-admin/tree/master
- 解压+在项目目录下打包dubbo-admin
mvn clean package -Dmaven.test.skip=true
打成一个jar包,可以直接运行【D:\Environment\java-linux\dubbo-admin-master\dubbo-admin\target目录下的jar】
- 下载zookeeper,在windows下运行
找到bin目录下----copy得到zoo.cfg
编辑zkServer.cmd 在最后面输入 pause,报错不是闪退而是展示错误
- 代码部分
- 建立一个空的项目
- 建立一个新的moudel—>provider-server的springboot项目+web依赖
- 建立另一个一个新的moudel—>cunsumer-server的springboot项目+web依赖
添加dubbo+zookeeper依赖
<!--Dubbo依赖 最新版 许多冲突需要进行排查-->
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>2.7.8</version>
</dependency>
<!--zkClient客户端依赖-->
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
<version>0.1</version>
<!--解决日志和zookeeper冲突-->
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--日志冲突-->
<!--zookeeper依赖-->
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>5.1.0</version>
<exclusions>
<!--解决与zookeeper的依赖冲突-->
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<!--3.6.0版本避免zookeeper与curator-framework依赖冲突-->
<version>3.6.1</version>
<!--排除日志冲突-->
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
application.properties配置
provider-server配置
# 应用服务 WEB 访问端口
server.port=8001
# 服务应用的名字
dubbo.application.name=provider-server
# 注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 哪些服务需要注册
dubbo.scan.base-packages=com.carter.service
cunsumer-server配置
# 应用服务 WEB 访问端口
server.port=8002
# 暴露自己的名字
dubbo.application.name=cunsumer-server
# 注册中心地址 哪里拿 远程改成远程
dubbo.registry.address=zookeeper://127.0.0.1:2181
接口com.carter.service的HelloService代码实现
public interface HelloService {
public String sayHello(String name);
}
接口实现类HelloServiceImpl
// 服务注册与发现
@Service// 可以被扫描到,在项目启动就可以注册到注册中心
@Component // 使用了dubbo后尽量不要用@Service进行注解
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello "+name;
}
}
- 进行远程连接
- 打开zkService
- 开启dubbo-admin
- 启动provider-server
- 登录 http://localhost:7001/进行注册中心查看
三、springboot+SpringSecurity整合
1、登录功能
导入依赖
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!--thymeleaf+springSecurity整合包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<!--security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--thymeleaf模板 start-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!--thymeleaf模板 end-->
代码实现
-
增加一个MyDetailUser的实体类
import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import java.util.Collection; @Data public class MyDetailUser extends User { private String phone; //手机号码(附赠品牌) public MyDetailUser(String username, String password, Collection<? extends GrantedAuthority> authorities) { super(username, password, authorities); } //构造方法username:账户 password密码 authorities权限 }
-
增加一个UserDetailServiceImpl实现方法
import com.carter.dao.UserDao;
import com.carter.pojo.MyDetailUser;
import com.carter.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.selectUserByUsername(username); //userDao中根据username查询整个user对象的方法
List<String> roleNameList; //根据user_id得到的权限集合
MyDetailUser detailUser = null; //springSecurity中的User自定义对象
List<SimpleGrantedAuthority> authorityList = new ArrayList<>(); //权限集合
if (user!=null){
roleNameList = userDao.selectRoleNameByUserId(user.getId()); //根据user_id得到对应的role_name权限集合
for (String roles : roleNameList) { //循环遍历User对应的所有权限
System.out.println("权限为--->"+roles); //进行输出打印
authorityList.add(new SimpleGrantedAuthority("ROLE_"+roles)); //ROLE_时必须添加的,将权限添加到springSecurity权限集合中
}
detailUser = new MyDetailUser(username, user.getPassword(), authorityList);
detailUser.setPhone("17369284516"); //根据情况进行springSecurity中User对象的改变
}
return detailUser;
}
}
- SecurityConfig中进行调用
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 授权
@Override
protected void configure(HttpSecurity http) throws Exception {
/**
* 定义规则 首页所有人能访问,功能也只有权限的人才能访问
* 请求授权的规则
* 表示以/level1/开头的url请求 只有vip1权限才能访问
* 强行执行【type=Forbidden禁止的, status=403】,报错
*/
http.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
/**
* 强行执行(没有权限)--->登录界面 http.formLogin()【默认】
* loginPage("/toLogin")【定制登录页面】
* 登录提交时候我们就要走login,而不是和loginPage的参数一样必须toLogin
* loginProcessingUrl("/login")定义提交的时候可以用login
* loginProcessingUrl = validate username and password 【必须是username和password才能接受表单数据】
* .usernameParameter("web表单中账户定义的name")【看源码】
*/
http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password");
/**
* 防止网站攻击:get注销不安全,【403错误】
* 关闭跨站防攻击
*/
http.csrf().disable();
/**
* 注销功能+清空Cookies 【.deleteCookies("remove").invalidateHttpSession(true)】
* 回到首页url
*/
http.logout().logoutSuccessUrl("/");
/**
* 开启记住我功能--->关闭浏览器任然存在【默认保存14天】
* 自定义接受前端参数
*/
http.rememberMe().rememberMeParameter("remember");
}
/**
* 2:认证,springboot version2.0.7稳定 可以直接使用 【其他版本 type=Internal Server Error, status=500】500错误
* 密码没有编码直接报错passwordEncoding
*在springSecurity 5.0+ 新增了很多加密方法
* passwordEncoder(new BCryptPasswordEncoder())加密方式
*/
@Autowired
private UserDetailServiceImpl userDetailService; //配置springSecurity中的User对象
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// ①内存中的身份验证
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("lsx").password(new BCryptPasswordEncoder().encode("1122")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("1122")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("1122")).roles("vip1");
//②数据库的认证方式---userDetailService调用
auth.userDetailsService(userDetailService).passwordEncoder(new BCryptPasswordEncoder());
}
}
前端主页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<!--记得导入sec的命名空间-->
<head>
<meta charset="UTF-8">
<title>首页界面</title>
</head>
<body>
<div style="text-align: center;">
<h3>欢迎来到首页</h3>
<br><br>
<div sec:authorize="hasRole('vip1')">
<h4 style="color: red">Level1</h4>
<a href="/level1/1">Level1-1</a>
<a href="/level1/2">Level1-2</a>
<a href="/level1/3">Level1-3</a>
<br><br>
</div>
<div sec:authorize="hasRole('vip2')">
<h4 style="color: green">Level2</h4>
<a href="/level2/1">Level2-1</a>
<a href="/level2/2">Level2-2</a>
<a href="/level2/3">Level12-3</a>
<br><br>
</div>
<div sec:authorize="hasRole('vip3')">
<h4 style="color: blue">Level3</h4>
<a href="/level3/1">Level3-1</a>
<a href="/level3/2">Level3-2</a>
<a href="/level3/3">Level3-3</a>
<br><br>
</div>
<!--未登录显示登录-->
<div sec:authorize="!isAuthenticated()">
<a href="/toLogin">登录</a>
</div>
<!--登录显示注销+用户名-->
<div sec:authorize="isAuthenticated()">
<a>用户名:<span sec:authentication="name"></span></a>
<br><br>
手机号码:
<span sec:authentication="principal.phone"/>
</div>
<!--登录显示注销-->
<div sec:authorize="isAuthenticated()">
<a href="/logout">注销</a>
</div>
</div>
</body>
</html>
2、注册功能
RouterController注册时查询可以选择的用户权限
@RequestMapping("/toRegister") //跳转至注册界面
public String toRegister(Model model){
List<Role> roleList = roleService.queryRoles(); //查询可选择的所有用户权限
model.addAttribute("roleList",roleList); //存到MOdel中
return "views/register";
}
前端register.html【html不支持EL表达式】
<html lang="en" xmlns:th="http://www.thymeleaf.org"> thymeleaf模板
<th colspan="2">
权限选择
<select name="roleId" id="roleId">
<option th:each="role : ${roleList}" th:id="${role.id}" th:value="${role.id}" th:text="${role.roleName}"></option>
</select>
</th>
Security配置登录失败跳转页面loginFailed
http.formLogin() //开启formLogin模式
.loginPage("/toLogin") //用户未登陆时,访问任何资源都会跳转到改路径,即为登录页面
.loginProcessingUrl("/login") //登录表单form中action的地址,也就是处理认证请求的路径
.usernameParameter("username") //表单name对应的值,springSecurity默认是username
.passwordParameter("password") //表单name对应的值,springSecurity默认是password
.defaultSuccessUrl("/") //登录成功跳转接口
.failureUrl("/loginFailed"); // 登陆失败跳转页面【默认跳转toLogin?error】
RouterController中的loginFailed方法
@RequestMapping("/loginFailed") //登录失败
public String loginFailed(Model model){
model.addAttribute("error","登录失败"); //登录失败提示信息
return "views/login";
}
四、springboot+shiro整合
五、聊一聊微服务
微服务架构问题?
分布式架构会遇到的4大核心问题?
1. 这么多服务,客户端如何去访问? // 网关
2. 这么多服务,服务之间如何通信? // Fegin
3. 这么多服务如何治理? // zookeepr
4. 服务挂了怎么办? // 熔断机制
解决方案:
springcloud,是一套生态,解决以上4个分布式问题
-
spring cloud NetFlix 一套解决方案 一站式解决方案
Api网关 zuul组件
Fegin ----> HTTPClient—> 基于HTTP通信方式,同步并阻塞
服务注册与发现 —> Eureka
熔断机制,Hystrix
2018年年底,NetFlix宣布无限期停止维护。生态不再维护,就会脱节
-
Apache Dubbo zookeeper 第二套解决系统
Api:没有!!! 要么找第三方组件,要么自己实现
Dubbo 是一个高性能的基于java实现的 RPC框架—>解决通信问题
服务注册与发现,zookeeper:动物园管理者(hadoop,Hive)
没有熔断机制!!!只能借助 Hystrix
-
SpringCloud Alibaba 一站式解决方案
-
又提出了一种方案===服务网格(下一代微服务标准 Server Mesh)
解决方案:istio (未来可能需要掌握)
分布式架构会遇到的4大核心问题?
1. 这么多服务,客户端如何去访问? // 网关
2. 这么多服务,服务之间如何通信? // Fegin
3. 这么多服务如何治理? // zookeepr
4. 服务挂了怎么办? // 熔断机制
解决方案:
springcloud,是一套生态,解决以上4个分布式问题
-
spring cloud NetFlix 一套解决方案 一站式解决方案
Api网关 zuul组件
Fegin ----> HTTPClient—> 基于HTTP通信方式,同步并阻塞
服务注册与发现 —> Eureka
熔断机制,Hystrix
2018年年底,NetFlix宣布无限期停止维护。生态不再维护,就会脱节
-
Apache Dubbo zookeeper 第二套解决系统
Api:没有!!! 要么找第三方组件,要么自己实现
Dubbo 是一个高性能的基于java实现的 RPC框架—>解决通信问题
服务注册与发现,zookeeper:动物园管理者(hadoop,Hive)
没有熔断机制!!!只能借助 Hystrix
-
SpringCloud Alibaba 一站式解决方案
-
又提出了一种方案===服务网格(下一代微服务标准 Server Mesh)
解决方案:istio (未来可能需要掌握)