目录
nginx动静分离
第1步:通过SwitchHosts新增二级域名:images.zmall.com
第2步:将本次项目的易买网所有静态资源js/css/images复制到nginx中的html目录下
第3步:在nginx的核心配置文件nginx.conf中新增二级域名images.zmall.com访问映射,用于实现nginx动静分离
注意:修改成功之后,重启nginx服务使其配置生效!!!
server{ listen 80; server_name images.zmall.com; location / { root html; index index.html; } }
检测静态资源服务器配置成功
http://images.zmall.com/css/style.css
第4步:删除zmall-product商品服务和zmall-gateway网关服务下的static静态资源,改用nginx中配置的静态资源
第5步:修改zmall-product商品服务中的templates/common/head.html
<#assign ctx> <#--域名,动态请求时需加入该前缀--> http://product.zmall.com </#assign> <#--采用H5方式的base标签,在整个页面的url地址前加入,用于访问nginx中的静态资源--> <base href="http://images.zmall.com/"/>
第6步:分别重启zmall-product、zmall-gateway以及nginx后输入请求地址:zmall.com/product-serv/index.html访问商品服务首页,如下所示:
如果出现IIS7,那么cmd窗口中执行下列指令
net stop w3svc
服务调用
创建配置zmall-cart购物车模块
第1步:基于Spring initializr创建zmall-cart购物车模块
第2步:将zmall-order订单模块配置到主模块中
<modules> ... <module>zmall-cart</module> ... </modules>
第3步:修改pom.xml
<parent> <groupId>com.zking.zmall</groupId> <artifactId>zmall</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>zmall-cart</artifactId> <dependencies> <dependency> <groupId>com.zking.zmall</groupId> <artifactId>zmall-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
第4步:配置application.yml(端口:8030)
server: port: 8030 spring: application: name: zmall-cart datasource: #type连接池类型 DBCP,C3P0,Hikari,Druid,默认为Hikari type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/zmall?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true username: root password: 1234 freemarker: suffix: .html template-loader-path: classpath:/templates/ cloud: nacos: config: server-addr: localhost:8848 #mybatis-plus配置 mybatis-plus: #所对应的 XML 文件位置 mapper-locations: classpath*:/mapper/*Mapper.xml #别名包扫描路径 type-aliases-package: com.zking.zmall.model configuration: #驼峰命名规则 map-underscore-to-camel-case: true #日志配置 logging: level: com.zking.zmall.mapper: debug
第5步:在启动类上加入@EnableDiscoveryClient
第6步:分别将购物车页面和common/head.html导入到templates目录,并修改head.html中的ctx局部变量
<#assign ctx> <#--一级域名,动态请求时需加入该前缀--> http://cart.zmall.com </#assign> <#--采用H5方式的base标签,在整个页面的url地址前加入,用于访问nginx中的静态资源--> <base href="http://images.zmall.com/"/>
第7步:在zmall-gateway网关服务中配置购物车的路由转发规则(重启gateway网关服务)
spring: application: name: zmall-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: routes: ... - id: cart_route uri: lb://zmall-cart # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略 predicates: - Path=/cart-serv/** filters: - StripPrefix=1 #此过滤器设置路由过滤器检查的请求属性,以确定是否应发送原始主机头,而不是由 HTTP 客户端确定的主机头 - PreserveHostHeader
注意:这里要配置过滤器PreserveHostHeader,用于处理重定向时依然已原始主机头发送请求。
第8步:创建CartController并定义请求方法
@Controller public class CartController { @RequestMapping("/cart.html") public String toCart(){ return "buyCar"; } @RequestMapping("/addCart") public String addCart(Integer pid,Integer num){ return "redirect:/cart.html"; } }
注意:这里使用redirect重定向方式跳转页面,在SpringCloud gateway路由转发过程中会导致域名跳转变成了http请求方式,所以必须在Gateway网关服务中进行相关的配置。具体请参考第8步的gateway网关路由配置。
第9步:在zmall-product模块中修改加入购物车的请求方法,定向到购物车
<td><a href="http://cart.zmall.com/addCart?pid=${(product.id)!}&num=3" class="b_sure">去购物车结算</a><a href="#" class="b_buy">继续购物</a></td>
创建配置zmall-order订单模块
第1步:基于Spring initializr创建zmall-order订单模块
第2步:将zmall-order订单模块配置到主模块中
<modules> ... <module>zmall-order</module> ... </modules>
第3步:修改pom.xml
<parent> <groupId>com.zking.zmall</groupId> <artifactId>zmall</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>zmall-order</artifactId> <dependencies> <dependency> <groupId>com.zking.zmall</groupId> <artifactId>zmall-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>
第4步:配置application.yml(端口:8040)
server: port: 8040 spring: application: name: zmall-order datasource: #type连接池类型 DBCP,C3P0,Hikari,Druid,默认为Hikari type: com.zaxxer.hikari.HikariDataSource driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/zmall?characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true username: root password: 1234 freemarker: suffix: .html template-loader-path: classpath:/templates/ cloud: nacos: config: server-addr: localhost:8848 #mybatis-plus配置 mybatis-plus: #所对应的 XML 文件位置 mapper-locations: classpath*:/mapper/*Mapper.xml #别名包扫描路径 type-aliases-package: com.zking.zmall.model configuration: #驼峰命名规则 map-underscore-to-camel-case: true #日志配置 logging: level: com.zking.zmall.mapper: debug
第5步:在启动类上加入@EnableDiscoveryClient
和@MapperScan({"com.zking.zmall.mapper"})
第6步:定义订单接口,可从公共模块zmall-common中直接复制过来
第7步:创建OrderController并定义请求接口
@Controller public class OrderController { @Autowired private IOrderService orderService; @RequestMapping("/orderUserList") @ResponseBody public List<Order> orderUserList(){ return orderService.list(new QueryWrapper<Order>() .eq("userId",18)); } }
第8步:在zmall-gateway网关服务中配置购物车的路由转发规则(重启gateway网关服务)
spring: application: name: zmall-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: routes: ... - id: order_route uri: lb://zmall-order # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略 predicates: - Path=/order-serv/** filters: - StripPrefix=1 - PreserveHostHeader
服务调用
在zmall-user中通过openfeign方式访问order服务接口
-
定义openfeign接口
@FeignClient("zmall-order") public interface IOrderFeignService { @RequestMapping("/orderUserList") List<Order> orderUserList(); }
-
启动类上设置
@EnableDiscoveryClient
和@EnableFeignClients
-
调用接口并测试接口
@Controller public class UserController { @Autowired private IOrderFeignService orderFeignService; @RequestMapping("/login.html") public String toLogin(){ return "login"; } @RequestMapping("/order.html") @ResponseBody public List<Order> orderUserList(){ return orderFeignService.orderUserList(); } }
测试链路
http://product.zmall.com/index.html http://product.zmall.com/product.html?pid=733 http://cart.zmall.com/cart.html
界面能够熊 商品微服务 直接跳转到 购物车微服务中
http://localhost:8010/order.html
调用 用户微服务接口 ,能访问 订单微服务的数据
spring session实战
什么是Spring Session
SpringBoot整合Spring-Session的自动配置可谓是开箱即用,极其简洁和方便。这篇文章即介绍SpringBoot整合Spring-Session,这里只介绍基于RedisSession的实战。
Spring Session 是Spring家族中的一个子项目,Spring Session提供了用于管理用户会话信息的API和实现。它把servlet容器实现的httpSession替换为spring-session,专注于解决 session管理问题,默认Session信息存储在Redis中,可简单快速且无缝的集成到我们的应用中
spring session官网地址:Spring Session
Spring Session的特性:
-
提供用户session管理的API和实现
-
提供HttpSession,以中立的方式取代web容器的session,比如tomcat中的session
-
支持集群的session处理,不必绑定到具体的web容器去解决集群下的session共享问题
为什么要使用Spring Session
SpringCloud微服务将一个完整的单体应用拆解成了一个个独立的子服务,而每一个独立的微服务子模块都将部署到不同的服务器中,而服务与服务之间是独立隔离的,这个时候使用要实现服务与服务之间的session会话共享,则需要借助于spring-session框架来解决分布式session管理与共享问题。
错误案例展示
在用户服务zmall-user中编写登录控制器,登录时创建session,并将当前登录用户存储sesion中。
@Controller public class UserController { @RequestMapping("/login.html") public String toLogin(HttpSession session){ session.setAttribute("username","admin"); return "login"; } }
在Gateway网关服务中添加用户服务的路由转发规则
spring: application: name: zmall-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: routes: ... - id: user_route uri: lb://zmall-user # lb指的是从nacos中按照名称获取微服务,并遵循负载均衡策略 predicates: - Path=/user-serv/** filters: - StripPrefix=1 - PreserveHostHeader
在商品服务zmall-product中编写查询控制器,在登录创建session后,使用将sessionId置于cookie中访问。如果没有session将返回错误。
@Controller public class ProductController { @RequestMapping("/index.html") public String index(Model model, HttpSession session){ Object username = session.getAttribute("username"); System.out.println("**********"+username); return "index"; } }
测试链路
#1.session信息存储 http://localhost:8010/login.html #2.session信息获取 http://product.zmall.com/index.html
配置spring-session
在公共模块zmall-common中引入spring-session的pom配置,由于spring-boot包含spring-session的starter模块,所以pom中依赖: 注意:公共模块作为所有微服务子模块的依赖支持,如果不在各服务模块中配置redis支持,会导致启动其他微服务时出现报错情况。
<!--redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--spring session--> <dependency> <groupId>org.springframework.session</groupId> <artifactId>spring-session-data-redis</artifactId> </dependency> <!--commons-pool2--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
分别在商品服务zmall-product和用户服务zmall-user中配置application.yml
spring: session: redis: flush-mode: on_save namespace: session.zmall cleanup-cron: 0 * * * * * store-type: redis timeout: 1800 redis: host: localhost port: 6379 password: 123456 jedis: pool: max-active: 100 max-wait: 10 max-idle: 10 min-idle: 10 database: 0
重新启动zmall-user和zmall-product服务,先访问:http://zmall.com/user-serv/login.html,然后在访问:http://zmall.com/product-serv/index.html;回到zmall-product模块控制台查看session获取情况。
注意:借助网关微服务内部可以访问,不同二级域名之间不可以访问
二级域名问题
测试在用户模块中保存用户信息,然后在产品模块中读取,但是使用二级域名方式访问读取失败
请分别在用户服务和商品服务中该配置类,解决二级域名访问session无效问题。
@Configuration public class SessionConfig { @Bean public CookieSerializer cookieSerializer(){ DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer(); cookieSerializer.setDomainName("zmall.com"); cookieSerializer.setCookieName("ZMALLSESSION"); return cookieSerializer; } @Bean public RedisSerializer<Object> springSessionDefaultRedisSerializer(){ return new GenericJackson2JsonRedisSerializer(); } }
测试链路
#1.先访问 http://user.zmall.com/login.html #2.后访问 http://product.zmall.com/index.html
用户登录
第1步:在zmall-common公共模块中创建全局异常处理、响应封装类
第2步:在zmall-user模块中定义IUserService及UserServiceImpl
IUserService
public interface IUserService extends IService<User> { JsonResponseBody<?> userLogin(UserVo user, HttpServletRequest req, HttpServletResponse resp); }
UserServiceImpl
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { @Override public JsonResponseBody<?> userLogin(UserVo user, HttpServletRequest req, HttpServletResponse resp) { //1.判断用户账号和密码是否为空 if(StringUtils.isEmpty(user.getLoginName())|| StringUtils.isEmpty(user.getPassword())) return new JsonResponseBody<>(JsonResponseStatus.USERNAME_OR_PWD_EMPTY); //2.根据用户名查询数据对应的用户信息 User us = this.getOne(new QueryWrapper<User>() .eq("loginName", user.getLoginName())); //3.判断us用户对象是否为空 if(null==us) return new JsonResponseBody<>(JsonResponseStatus.USERNAME_ERROR); try { //MD5加密转换处理 String pwd=MD5Utils.md5Hex(user.getPassword().getBytes()); //4.判断输入密码与数据库表存储密码是否一致 if(!us.getPassword().equals(pwd)){ return new JsonResponseBody<>(JsonResponseStatus.PASSWORD_ERROR); } } catch (Exception e) { e.printStackTrace(); return new JsonResponseBody<>(JsonResponseStatus.ERROR); } //5.通过UUID生成token令牌并保存到cookie中 String token= UUID.randomUUID().toString().replace("-",""); //将随机生成的Token令牌保存到Cookie中,并设置1800秒超时时间 CookieUtils.setCookie(req,resp,"token",token,7200); //6.将token令牌与spring session进行绑定并存入redis中 HttpSession session = req.getSession(); session.setAttribute(token,us); return new JsonResponseBody<>(token); } }
第3步:创建UserVo类
@Data public class UserVo { private String loginName; private String password; }
第4步:在UserController中定义用户登录方法
/** * 用户登陆功能实现 * @return */ @RequestMapping("/userLogin") @ResponseBody public JsonResponseBody<?> userLogin(UserVo user, HttpServletRequest req, HttpServletResponse resp){ return userService.userLogin(user,req,resp); }
第5步:在前端login.html页面中定义登录js方法
<script> $(function(){ $('.log_btn').click(function(){ let loginName=$('.l_user').val(); let password=$('.l_pwd').val(); if(''===loginName){ alert('请输入用户名!'); return false; } if(''===password){ alert('请输入密码!'); return false; } console.log({ loginName:loginName, password:password }); $.post('http://zmall.com/user-serv/userLogin',{ loginName:loginName, password:password },function(rs){ console.log(rs); if(rs.code===200){ location.href='http://zmall.com/product-serv/index.html'; }else{ alert(rs.msg); } },'json'); }); }); </script>
登录成功后,跳转到商品首页
http://zmall.com/user-serv/login.html
如果用二级域名进行测试,那么需要解决跨域问题;
url: jdbc:mysql://localhost:3306/zmall?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC