EGO电商复习

文章目录

  • EGO电商复习
    • 一、电商项目介绍
    • 二. 数据库准备和逆向工程
    • 三. 环境搭建
      • 搭建 maven 环境
      • 创建项目
      • MyBatis 分页插件
      • 实现商品分页显示功能
      • 我们先来看看Dubbo:
        • 一.SOA
        • 二. RPC
        • 三. Dubbo简介
        • 四.Dubbo支持的注册中心
        • 五.Zookeeper讲解
        • 六. Dubbo支持的协议
        • 七.Dubbo中Provider搭建
        • 八. Admin管理界面
        • 九.Consumer搭建过程
      • Maven打包插件Assembly
      • 实现商品新增-图片上传
        • 先来看看vsftpd:
          • 一.VSFTPD 简介
          • 二. FtpClient
          • 三. 反向代理和正向代理
          • 三.nginx 配置(已经安装完成)
      • jsonp跨域请求
      • Redis缓存
        • 一.Redis 简介
        • 二. Redis 常用命令
        • 三. Redis 几个常用概念
        • 四 . Jedis
        • 五. Jedis 集群
        • 六. redis应用实例
      • Junit 4
    • Solr
      • 一. 简介
      • 二. IK Analyzer 中文拆词器
      • 三. Solr 管理界面的 documents 菜单功能
      • 四. 管理界面 quey 菜单功能
      • 五. SolrCloud
      • 六. 应用实例
        • 一.商品搜索功能业务分析
        • 二. 配置 Solr 的 Field
        • 三. 创建 ego-search 项目
        • 四.Solr 数据初始化
    • HttpClient
      • HttpClient 简介
      • 示例代码:
      • HttpCilentUtils工具类
    • SSO(单点登录)
      • 一. SSO 简介
      • 二. Cookie 复习
      • 三. HttpSession 运行原理.
    • MyCat
      • 一.MySQL 主从简介
      • 二. MyCat 简介
    • 工具类汇总:
      • CookieUtils
      • FtpUtils
      • HttpClientUtils
      • IDUtils
      • JsonUtils
    • 项目部署
      • 一. 部署 Dubbo
      • 二. 设置 redis 开机自启动
      • 三. 使用 Nginx 实现负载均衡
    • 四.部署 ego-portal

一、电商项目介绍

  1. 电商行业的几种模式.
    1.1 B2B: 企业到企业, 商家到商家。 代表: 阿里巴巴、 慧聪网。
    1.2 B2C: 商家到客户。 代表: 京东、 淘宝商城(B2B2C)。
    1.3 C2C: 客户到客户。 淘宝集市。
    1.4 O2O: 线上到线下。

  2. 技术选型
    2.1 Spring、 SpringMVC、 Mybatis
    2.2 JSP、 JSTL、 jQuery、 jQuery plugin、 EasyUI、 KindEditor(富文本编辑器)、 CSS+DIV
    2.3 Redis(缓存服务器)
    2.4 Solr(搜索)
    2.5 Dubbo(调用系统服务)
    2.6 Mysql
    2.7 Nginx(web 服务器)
    2.8 jsonp 跨域数据请求格式
    2.9 nexus maven 私服
    2.10 MyBatis 逆向工程
    2.11 HttpClient 使用 java 完成请求及响应的技术.
    2.12 MyCat mysql 分库分表技术

  3. 开发工具和环境
    3.1 Eclipse mars
    3.2 Maven 3.3.3
    3.3 Tomcat 7.0.79(Maven Tomcat Plugin)
    3.4 JDK 1.7
    3.5 Mysql 5.7
    3.6 Nginx 1.8.0
    3.7 Redis 3.0.0
    3.8 Win10 操作系统
    3.9 Linux(服务器系统)

  4. 人员配置
    4.1 产品经理: 3 人, 确定需求以及给出产品原型图。
    4.2 项目经理: 1 人, 项目管理。
    4.3 前端团队: 5 人, 根据产品经理给出的原型制作静态页面。
    4.4 后端团队: 20 人, 实现产品功能。
    4.5 测试团队: 5 人, 测试所有的功能。
    4.7 运维团队: 3 人, 项目的发布以及维护。

  5. 项目周期:
    5.1 6 个月

  6. 整个电商结构图

  1. 基于 SOA 架构

二. 数据库准备和逆向工程

  1. 直接运行 SQL 脚本.
  2. 使用逆向工程生成 mapper 和 pojo

三. 环境搭建

搭建 maven 环境

  1. 为什么使用 Nexus 搭建 maven 私服(私服的作用)
    1.1 公司所有开发成员没有外网,通过局域网连接 nexus 私服,由私服连接外网
    1.2 把项目发布到私服.其他人员从私服下载.

  2. 使用私服之后架构图

  1. 搭建 nexus 的步骤
    3.1 nexus-2.12.0-01-bundle.zip 解压到任意非中文目录中
    3.2 修改 nexus 端口(默认 8081)

    ​ 3.2.1 nexus-2.12.0-01\conf\nexus.properties

    3.3 粘贴索引库(不配置无法搜索)
    3.3.1 先清空 sonatype-work\nexus\indexer\central-ctx 内容
    3.3.1 把解压后的索引文件粘贴到这个文件夹中
    3.4 进入 nexus-2.12.0-01\bin\jsw\windows-x86-64(对应自己系统)
    3.4.1 install-nexus.bat 安装服务中央仓库nexus
    3.4.2 start-nexus.bat 开启服务
    3.4.3 stop-nexus.bat 停止服务
    3.4.4 uninstall-nexus.bat 卸载服务
    3.5 在浏览器输入 http://localhost:8091/nexus
    3.6 点击右侧 log in ,输入用户名:admin,密码:admin123
    3.7 在左侧搜索框中输入 artifact id 测试是否配置成功

  1. 使用 maven 连接私服

    4.1 前提:把 maven环境搭建,并设置 users settings引用 settings.xml
    4.2 在 settings.xml 配置
    4.2.1 本地仓库路径

    <localRepository>D:/maven/myrepository</localRepository>
    

    ​ 4.2.2 配置 jdk

    <profile><id>jdk-1.7</id><activation><activeByDefault>true</activeByDefault><jdk>1.7</jdk></activation><properties><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target><maven.compiler.compilerVersion>1.7</maven.compiler.compilerVersion></properties>
    </profile>
    

    ​ 4.2.3 配置私服构建(连接私服用的到 jar 等内容)

    <profile><id>nexusTest</id><repositories><repository><id>local-nexus</id><url>http://127.0.0.1:8091/nexus/content/groups/public/</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories>
    </profile>
    

    ​ 4.2.4 配置让私服构建生效
    ​ 4.2.4.1 nexusTest 上面<profile>的<id>

    <activeProfiles> <!--激活 id 为 nexusTest 的 profile--><activeProfile>nexusTest</activeProfile>
    </activeProfiles>
    

    ​ 4.2.5 配置镜像,maven 连接私服

    <mirror><id>nexus-releases</id><mirrorOf>*</mirrorOf><url>http://localhost:8091/nexus/content/groups/public</url>
    </mirror>
    <mirror><id>nexus-snapshots</id><mirrorOf>*</mirrorOf><url>http://localhost:8091/nexus/content/repositories/apache-snapshots/</url>
    </mirror>
    
  2. 把项目发布到私服的步骤
    5.1 在 pom.xml 中配置私服路径

    <distributionManagement><repository><id>releases</id><url>http://localhost:8091/nexus/content/repositories/releases</url></repository><snapshotRepository><id>snapshots</id><url>http://localhost:8091/nexus/content/repositories/snapshots</url></snapshotRepository>
    </distributionManagement>
    

    5.2 在 settings.xml 中配置连接私服仓库的用户名和密码
    5.2.1 <server>中<id>和 pom.xml 中<repository>中<id>对应

    <server><id>releases</id><username>admin</username><password>admin123</password>
    </server>
    <server><id>snapshots</id><username>admin</username><password>admin123</password>
    </server>
    

    5.3 右键项目–> run as 输入 deploy

  3. 最终的setting.xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<localRepository>D:/Maven/maven-ego/Repository</localRepository><pluginGroups></pluginGroups><proxies></proxies><servers><server>  <id>releases</id>  <username>admin</username>  <password>admin123</password>  </server>  <server>  <id>snapshots</id>  <username>admin</username>  <password>admin123</password>  </server>  </servers><mirrors><mirror>     <id>nexus-releases</id>     <mirrorOf>*</mirrorOf>     <url>http://localhost:8091/nexus/content/groups/public</url>     </mirror> <mirror>     <id>nexus-snapshots</id>     <mirrorOf>*</mirrorOf>     <url>http://localhost:8091/nexus/content/repositories/apache-snapshots/</url>     </mirror> </mirrors><profiles><profile><id>jdk-1.7</id><activation><activeByDefault>true</activeByDefault><jdk>1.7</jdk></activation><properties><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target><maven.compiler.compilerVersion>1.7</maven.compiler.compilerVersion></properties></profile><profile><id>nexusTest</id><repositories><repository><id>local-nexus</id><url>http://127.0.0.1:8091/nexus/content/groups/public/</url><releases><enabled>true</enabled></releases><snapshots><enabled>true</enabled>  </snapshots> </repository></repositories></profile></profiles><activeProfiles> <!--激活id为nexusTest的profile-->
<activeProfile>nexusTest</activeProfile>
</activeProfiles></settings>

创建项目

  1. 创建六个项目(我这里是所有项目)
    1.1 ego-commons: 放工具类等
    1.2 ego-manage: 后台项目
    1.3 ego-parent:父项目
    1.4 ego-pojo:实体类
    1.5 ego-service: 服务接口
    1.6 ego-service-impl: dubbo 的 provider

    1.7 ego-item 商品管理项目

    1.8 ego-order 订单项目

    1.9 ego-cart 购物车项目

    1.10 ego-portal

    1.11 ego-redis 缓存

    1.12 ego-passport

    1.13 ego-search 搜索

  1. 把后台页面放在 ego-manage/WEB-INF 中

  2. 在 ego-manage 编写控制器类

    @Controller
    public class PageController {@RequestMapping("/")public String welcome(){return "index";}@RequestMapping("{page}")public String showPage(@PathVariable String page){return page;}
    }
    

MyBatis 分页插件

  1. 在 mybatis.xml中配置标签,在程序员所编写的 sql命令基础上添加一些内容.

  2. 在 pom.xml 配置依赖

    <!-- 分页插件 -->
    <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>4.1.6</version>
    </dependency>
    
  3. 创建 mybatis.xml 并配置插件信息

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
    <configuration><plugins><plugin interceptor="com.github.pagehelper.PageHelper"><!-- 告诉分页插件是哪个数据库 --><property name="dialect" value="mysql"/></plugin></plugins>
    </configuration>
    
  4. 在 applicationContext.xml 中配置加载 mybatis.xml

    <!-- SqlSessionFactory -->
    <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"></property><property name="typeAliasesPackage" value="com.ego.pojo"></property><property name="configLocation" value="classpath:mybatis.xml"></property>
    </bean>
    
  5. 编写代码时注意: PageHelper.startPage()写在查询全部上面

    PageHelper.startPage(page, rows);//查询全部
    List<TbItem> list = tbItemMapper.selectByExample(new TbItemExample());
    //分页代码
    //设置分页条件
    PageInfo<TbItem> pi = new PageInfo<>(list);
    

实现商品分页显示功能

  1. 在 ego-commons 中创建 easyuidatagrid 类
    1.1 把所有 ego-pojo 中类序列化

    public class EasyUIDataGrid implements Serializable {//当前页显示数据private List<?> rows;//总条数private long total;
    }
    
  2. 在 ego-service 中创建接口

    public interface TbItemDubboService {/*** 商品分页查询* @param page* @param rows* @return*/EasyUIDataGrid show(int page,int rows);
    }
    
  3. 在 ego-service-impl 编写功能

    public class TbItemDubboServiceImpl implements TbItemDubboService {@Resourceprivate TbItemMapper tbItemMapper;@Overridepublic EasyUIDataGrid show(int page, int rows) {PageHelper.startPage(page, rows);//查询全部List<TbItem> list = tbItemMapper.selectByExample(new TbItemExample());//分页代码//设置分页条件PageInfo<TbItem> pi = new PageInfo<>(list);//放入到实体类EasyUIDataGrid datagrid = new EasyUIDataGrid();datagrid.setRows(pi.getList());datagrid.setTotal(pi.getTotal());return datagrid;}
    }
    
  4. 在 ego-service-impl 中 applicationContext-dubbo.xml 配置接口

    <dubbo:service interface="com.ego.dubbo.service.TbItemDubboService" ref="tbItemDubboServiceImpl"></dubbo:service>
    <bean id="tbItemDubboServiceImpl"class="com.ego.dubbo.service.impl.TbItemDubboServiceImpl"></bean>
    

我们先来看看Dubbo:

一.SOA
  1. 英文名称(Service Oriented Ambiguity)

  2. 中文名称:面向服务架构

    2.1 有一个专门提供服务单元.

    2.2 其他所有单元都调用这个服务.

  3. SOA定位:

    3.1 如何**设计项目****,**让开发时更有效率.

    3.2 SOA是一种思想

  4. 之前项目架构设计

    4.1 在公司项目不允许所有项目都访问数据库.

    4.2 开发时,数据库访问层代码可能出现冗余

  1. 使用SOA架构

    5.1 专门访问数据库服务(项目).

    5.2 开发时可以实现,数据访问控制和代码复用.

  2. 实现SOA架构时,常用服务.

    6.1 Dubbo 做为服务.

    6.2 WebService 做为服务.

    6.3 Dubbox 做为服务.

    6.4 服务方就是web项目,调用web项目的控制器.

    ​ 6.4.1 使用HttpClient可以调用其他项目的控制器.

二. RPC
  1. 英文名称(Remote Procedure Call Protocol)

  2. 中文名称:远程过程调用协议

  3. RPC解析:客户端(A)通过互联网调用远程服务器,不知道远程服务器具体实现,只知道远程服务器提供了什么功能.

  4. RPC最大优点:

    4.1 数据安全性.

三. Dubbo简介
  1. Dubbo:一个分布式、高性能、透明化的RPC服务框架

  2. 作用:提供服务自动注册、自动发现等高效服务治理方案.

  3. Dubbo架构图

    3.1 Provider :提供者,服务发布方.

    3.2 Consumer:消费者, 调用服务方

    3.3 Container:Dubbo容器.依赖于Spring容器.

    3.4 Registry: 注册中心.当Container启动时把所有可以提供的服务列表上Registry中进行注册.

    ​ 3.4.1 作用:告诉Consumer提供了什么服务和服务方在哪里.

    3.5 Monitor:监听器

    3.6 虚线都是异步访问,实线都是同步访问

    3.7 蓝色虚线:在启动时完成的功能

    3.8 红色虚线(实线)都是程序运行过程中执行的功能

    3.9 所有的角色都是可以在单独的服务器上.所以必须遵守特定的协议.

  1. 运行原理:

    4.0 启动容器,相当于在启动Dubbo的Provider

    4.1 启动后会去注册中心进行注册.注册所有可以提供的服务列表

    4.2 在Consumer启动后会去Registry中获取服务列表和Provider的地址.进行订阅.

    4.3 当Provider有修改后,注册中心会把消息推送给Consummer

    ​ 4.3.1 使用了观察者设计模式(又叫发布/订阅设计模式)

    4.4 根据获取到的Provider地址,真实调用Provider中功能.

    ​ 4.4.1 在Consumer方使用了代理设计模式.创建一个Provider方类的一个代理对象.通过代理对象获取Provider中真实功能,起到保护Provider真实功能的作用.

    4.5 Consumer和Provider每隔1分钟向Monitor发送统计信息,统计信息包含,访问次数,频率等.

四.Dubbo支持的注册中心
  1. Zookeeper

    1.1 优点:支持网络集群

    1.2 缺点:稳定性受限于Zookeeper

  2. Redis

    2.1 优点:性能高.

    2.2 缺点:对服务器环境要求较高.

  3. Multicast

    3.1 优点:面中心化,不需要额外安装软件.

    3.2 缺点:建议同机房(局域网)内使用

  4. Simple

    4.1 适用于测试环境.不支持集群.

五.Zookeeper讲解
  1. Zookeeper 分布式协调组件.

    1.1 本质一个软件.

  2. Zookeeper常用功能

    2.1 发布订阅功能.把zookeeper当作注册中心原因.

    2.2 分布式/集群管理功能.

  3. 使用java语言编写的.

六. Dubbo支持的协议
  1. Dubbo

    1.1 Dubbo官方推荐的协议.

    1.2 本质:使用NIO和线程池进行处理.

    1.3 缺点:大文件传输时可能出现文件传输失败问题.

  2. RMI

    2.1 JDK提供的协议,远程方法调用协议.

    2.2 缺点:偶尔连接失败.

    2.3 优点:JDK原生,不需要进行额外配置(导入jar)

  3. Hession

    3.1 优点:基于http协议,http请求支持.

    3.2 缺点:需要额外导入jar,并在短连接时性能低

七.Dubbo中Provider搭建
  1. 新建Maven Project, 里面只有接口(dubbo-service)

    1.1 为什么这么做?

    ​ RPC框架,不希望Consumer知道具体实现.如果实现类和接口在同一个项目中,Consumer依赖这个项目时,就会知道实现类具体实现.

  2. 新建Maven Project, 写接口的实现类(dubbo-service-impl)

  3. 在duboo-service-impl中配置pom.xml

    3.1 依赖接口

    3.2 依赖dubbo,去掉老版本spring

    3.3 依赖新版本spring

    3.4 依赖zookeeper客户端工具zkClient

    <dependencies><dependency><groupId>com.bjsxt</groupId><artifactId>dubbo-service</artifactId><version>0.0.1-SNAPSHOT</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>dubbo</artifactId><version>2.5.3</version><exclusions><exclusion><artifactId>spring</artifactId><groupId>org.springframework</groupId></exclusion></exclusions></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>4.1.6.RELEASE</version></dependency><!-- 访问zookeeper的客户端jar --><dependency><groupId>com.101tec</groupId><artifactId>zkclient</artifactId><version>0.10</version></dependency></dependencies>
  4. 新建实现类,并实现接口方法.

  5. 新建配置文件applicationContext-dubbo.xml,并配置

    5.1 dubbo:application/ 给provider起名,在monitor或管理工具中区别是哪个provider

    5.2 dubbo:registry/ 配置注册中心

    ​ 5.2.1 address:注册中心的ip和端口

    ​ 5.2.2 protocol使用哪种注册中心

    5.3 dubbo:protocol/ 配置协议

    ​ 5.3.1 name 使用什么协议

    ​ 5.3.2 port: consumer invoke provider时的端口号

    5.4 dubbo:service/ 注册接口

    ​ 5.4.1 ref 引用接口实现类<bean>的id值

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"><!-- 给当前Provider自定义个名字 --><dubbo:application name="dubbo-ego-service"/><!-- 配置注册中心  --><dubbo:registry address="192.168.43.114:2181" protocol="zookeeper"></dubbo:registry><!-- 配置端口 --><dubbo:protocol name="dubbo" port="20888" ></dubbo:protocol><!-- 注册功能 --><!-- 商品信息 --><dubbo:service interface="com.ego.dubbo.service.TbItemDubboService" ref="tbItemDubboServiceImpl" timeout="1200000"></dubbo:service><bean id="tbItemDubboServiceImpl" class="com.ego.dubbo.service.impl.TbItemDubboServiceImpl"></bean><!-- 商品描述 --><dubbo:service interface="com.ego.dubbo.service.TbItemDescDubboService" ref="tbItemDescDubboServiceImpl" timeout="1200000"></dubbo:service><bean id="tbItemDescDubboServiceImpl" class="com.ego.dubbo.service.impl.TbItemDescDubboServiceImpl"></bean><!-- 商品类目 --><dubbo:service interface="com.ego.dubbo.service.TbItemCatDubboService" ref="tbItemCatDubboServiceImpl" timeout="1200000"></dubbo:service><bean id="tbItemCatDubboServiceImpl" class="com.ego.dubbo.service.impl.TbItemCatDubboServiceImpl"></bean><!-- 规格参数 --><dubbo:service interface="com.ego.dubbo.service.TbItemParamDubboService" ref="tbItemParamDubboServiceImpl" timeout="1200000"></dubbo:service><bean id="tbItemParamDubboServiceImpl" class="com.ego.dubbo.service.impl.TbItemParamDubboServiceImpl"></bean><!-- 内容分类 --><dubbo:service interface="com.ego.dubbo.service.TbContentCategoryDubboService" ref="tbContentCategoryDubboServiceImpl" timeout="1200000"></dubbo:service><bean id="tbContentCategoryDubboServiceImpl" class="com.ego.dubbo.service.impl.TbContentCategoryDubboServiceImpl"></bean><!-- 内容管理 --><dubbo:service interface="com.ego.dubbo.service.TbContentDubboService" ref="tbContentDubboServiceImpl" timeout="1200000"></dubbo:service><bean id="tbContentDubboServiceImpl" class="com.ego.dubbo.service.impl.TbContentDubboServiceImpl"></bean><!-- 商品规格参数 --><dubbo:service interface="com.ego.dubbo.service.TbItemParamItemDubboService" ref="tbItemParamItemDubboServiceImpl" timeout="1200000"></dubbo:service><bean id="tbItemParamItemDubboServiceImpl" class="com.ego.dubbo.service.impl.TbItemParamItemDubboServiceImpl"></bean><!-- 用户登录 --><dubbo:service interface="com.ego.dubbo.service.TbUserDubboService" ref="tbUserDubboServiceImpl" timeout="1200000"></dubbo:service><bean id="tbUserDubboServiceImpl" class="com.ego.dubbo.service.impl.TbUserDubboServiceImpl"></bean><!-- 订单管理 --><dubbo:service interface="com.ego.dubbo.service.TbOrderDubboService" ref="tbOrderDubboServiceImpl" timeout="1200000"></dubbo:service><bean id="tbOrderDubboServiceImpl" class="com.ego.dubbo.service.impl.TbOrderDubboServiceImpl"></bean><import resource="../../applicationContext.xml"/>
    </beans>
    1. 启动容器

      6.1 通过spring方式启动

      ​ 6.1.1 applicationContext-dubbo.xml位置没有要求

      ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-dubbo.xml");ac.start();System.out.println("启动成功");System.in.read();

      6.2 使用dubbo提供的方式启动(推荐使用这种方式)

      6.2.1 要求applicationContext-dubbo.xml必须放入类路径下/META-INF/spring/*.xml

      package com.ego.test;import com.alibaba.dubbo.container.Main;public class Test {public static void main(String[] args) {Main.main(args);}
      }
八. Admin管理界面
  1. 本质就是一个web项目

  2. 获取注册中心内Provider注册的信息.用页面呈现出来.

  3. 实现步骤

    3.1 把dubbo-admin-2.5.3.war上传到服务器tomcat中.

    3.2 启动tomcat完成后关闭tomcat,删除上传的dubbo-admin-2.5.3.war

    ​ 3.2.1 为什么要删除:需要修改解压后的文件夹,如果不删除.war文件,下次重启tomcat会还原成未修改状态

    3.3 进入dubbo-admin-2.5.3/WEB-INF/dubbo.properties,修改第一行为zookeeper的ip和端口

    ​ 3.3.1 第二行和第三行为管理界面的用户名和密码

​ 3.4 启动tomcat, 在浏览器地址栏访问tomcat中dubbo项目

九.Consumer搭建过程
  1. 在pom.xml中除了ssm的依赖添加dubbo相关3个依赖(接口,dubbo.jar,zkClient)

  2. web.xml中修改<init-value>applicationContext-*.xml

    <context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext-*.xml</param-value>
    </context-param>
  3. spring配置文件命名为applicationContext-spring.xml,配置dubbo的配置文件applicationContext-dubbo.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd"><!-- 给当前Provider自定义个名字 --><dubbo:application name="dubbo-manage"/><!-- 配置注册中心  --><dubbo:registry address="192.168.43.114:2181" protocol="zookeeper"></dubbo:registry><!-- 配置注解扫描 --><dubbo:annotation package="com.ego.item.service.impl"/></beans>
  4. 不需要编写mapper

  5. 除了ServiceImpl中引用Provider中接口对象改变,其他代码都一样.

    @Service
    public class TestServiceImpl implements TestService {//  @Resource
    //  private xxMapper xxxMapper;@Referenceprivate DemoService demoService;

Maven打包插件Assembly

  1. 在dubbo的provider项目(实现类项目)中pom.xml配置assembly插件信息

    <!-- 指定项目的打包插件信息 --><plugin><artifactId>maven-assembly-plugin</artifactId><configuration><!-- 指定打包描述文件的位置:相对项目根目录的路径 --><!-- assembly打包的描述文件 --><descriptor>assembly/assembly.xml</descriptor></configuration><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plug2in></plugins>
  2. 在项目根目录下新建assembly文件夹

  1. 在assembly文件夹中新建assembly.xml
<?xml version='1.0' encoding='UTF-8'?>
<assembly    xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd"><!-- 该字符会添加到最终tar.gz包的名称后面,作为后缀 --><id>assembly</id><!-- 指定打包的格式为tar.gz,该类型压缩包在linux中比较常见 --><formats><format>tar.gz</format></formats><!-- 在tar.gz压缩包中是否包含根文件夹,该根文件夹名称和tar.gz去掉id后缀一致 --><includeBaseDirectory>true</includeBaseDirectory><fileSets><!-- 将项目根路径下assembly/bin路径中的内容打包到压缩包中的根目录下的bin目录中 --><fileSet><!-- 相对项目根路径的相对路径 --><directory>assembly/bin</directory><outputDirectory>bin</outputDirectory><!-- 设置最终tar.gz中该文件夹下的权限,跟linux权限写法一致 --><fileMode>0755</fileMode></fileSet><!-- 将项目根路径下assembly/conf路径中的内容打包到压缩包中的根目录下的conf目录中 --><fileSet><directory>assembly/conf</directory><outputDirectory>conf</outputDirectory><!-- 设置其linux权限 --><fileMode>0644</fileMode></fileSet></fileSets><!-- 将所有依赖的jar包打包到压缩包中的根目录下的lib目录中 --><!-- 此lib目录中包含自己开发的项目jar包以及demo_service.jar,还有第三方的jar包 --><dependencySets><dependencySet><outputDirectory>lib</outputDirectory></dependencySet></dependencySets>
</assembly>
  1. 解压下载的dubbo-monitor-simple-2.5.3-assembly.tar.gz压缩包,把解压后的bin和conf粘贴到项目下assembly文件夹中.

    4.1 清空conf/dubbo.properties中内容.

  2. 右键项目–> maven install

    5.1 在target下出现: 项目名-版本-assembly.tar.gz压缩包

  3. 把压缩包复制到window或linux中

    6.1 window中使用start.bat启动,关闭命令窗口关闭服务.

    6.2 linux中使用start.sh启动使用stop.sh关闭.


实现商品新增-图片上传

先来看看vsftpd:
一.VSFTPD 简介
  1. Linux 的组件(一款软件),安装到 Linux 后通过 java 代码(FtpClient)实现文件上传.

  2. VSFTPD 基于 FTP 协议

  3. 为什么要使用 VSFTPD
    3.1 之前实现文件上传

​ 3.2 使用 VSFTPD 后优化

​ 3.2.1 如果希望在客户端直接访问图片服务器中的图片,由于VSFTPD 是基于 FTP 协议的,客户端浏览器是需要通过 http 协议访
问图片.
​ 3.2.1.1 解决办法使用 Nginx 进行反向代理.

  1. 可以使用格式在浏览器中访问到对应的图片(不推荐这样使用)
    4.1 在谷歌浏览器中直接访问到 ftpuser 目录
    4.2 在 IE 中访问的是 linux 的根目录

    ftp://ftpuser:ftpuser@192.168.139.131
    
二. FtpClient
  1. java 技术,使用 FtpClient 使用使用 java 代码上传文件到 vsftpd 服务端 2.

  2. 代码示例

    FTPClient ftp = new FTPClient();
    //设置 ip 和端口,写在用户名和密码上面
    ftp.connect("192.168.139.131", 21);
    //设置用户名和密码
    ftp.login("ftpuser", "ftpuser");
    //设置文件类型
    ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
    InputStream is = new FileInputStream("E:/timg.jpg");
    //第一个参数存储时名称
    ftp.storeFile("abc.jpg", is);
    //退出
    ftp.logout();
    
  3. 使用 web 项目结合 FTPClient 完成

三. 反向代理和正向代理
  1. 正向代理
    1.1 客户端知道最终要访问的服务器地址.

  2. 反向代理
    1.2 客户端只知道代理服务器地址,而不知道真实访问的服务器地址.

三.nginx 配置(已经安装完成)
  1. 进入到 nginx/conf 文件夹修改 nginx.conf
    1.1 赋予 ftpuser 用户权限

1.2 设置代理的目录
1.2.1 root : 代理的目录
1.2.2 index: 欢迎项
1.2.3 如果只配置 root 不配置 index,在浏览器输入 ip 后依然报403


  1. 需要在 linux 中安装 vsftpd,安装后实现使用 ftpclient 完成图片上传功能

  2. 在 vsftpd 所在服务器安装 nginx,实现图片回显

  3. 在 ego-parent 中 pom.xml 引入 ftpclient 的 jar

    <ftpclient-version>3.3</ftpclient-version>
    <!-- ftpclient -->
    <dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId><version>${ftpclient-version}</version>
    </dependency>
    
  4. 在 ego-commons 中 pom.xml 中引入 ftpclient 的 jar

    <dependencies><dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId></dependency>
    </dependencies>
    
  5. 在 ego-commons 中 com.ego.commons.utils 添加两个工具类

    5.1 FtpUtil

package com.ego.commons.utils;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;/*** ftp上传下载工具类*/
public class FtpUtil {/** * Description: 向FTP服务器上传文件 * @param host FTP服务器hostname * @param port FTP服务器端口 * @param username FTP登录账号 * @param password FTP登录密码 * @param basePath FTP服务器基础目录* @param filePath FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath* @param filename 上传到FTP服务器上的文件名 * @param input 输入流 * @return 成功返回true,否则返回false */
public static boolean uploadFile(String host, int port, String username, String password, String basePath,String filePath, String filename, InputStream input) {boolean result = false;FTPClient ftp = new FTPClient();try {int reply;ftp.connect(host, port);// 连接FTP服务器// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器ftp.login(username, password);// 登录reply = ftp.getReplyCode();if (!FTPReply.isPositiveCompletion(reply)) {ftp.disconnect();    return result;}//切换到上传目录if (!ftp.changeWorkingDirectory(basePath+filePath)) {//如果目录不存在创建目录String[] dirs = filePath.split("/");String tempPath = basePath;for (String dir : dirs) {if (null == dir || "".equals(dir)) continue;tempPath += "/" + dir;if (!ftp.changeWorkingDirectory(tempPath)) {if (!ftp.makeDirectory(tempPath)) {return result;} else {ftp.changeWorkingDirectory(tempPath);}}}}//设置上传文件的类型为二进制类型ftp.setFileType(FTP.BINARY_FILE_TYPE);//上传文件if (!ftp.storeFile(filename, input)) {System.out.println("second:" + result);return result;}input.close();ftp.logout();result = true;} catch (IOException e) {e.printStackTrace();} finally {if (ftp.isConnected()) {try {ftp.disconnect();} catch (IOException ioe) {}}}System.out.println("end:" + result);return result;}/** * Description: 从FTP服务器下载文件 * @param host FTP服务器hostname * @param port FTP服务器端口 * @param username FTP登录账号 * @param password FTP登录密码 * @param remotePath FTP服务器上的相对路径 * @param fileName 要下载的文件名 * @param localPath 下载后保存到本地的路径 * @return */  public static boolean downloadFile(String host, int port, String username, String password, String remotePath,String fileName, String localPath) {boolean result = false;FTPClient ftp = new FTPClient();try {int reply;ftp.connect(host, port);// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器ftp.login(username, password);// 登录reply = ftp.getReplyCode();if (!FTPReply.isPositiveCompletion(reply)) {ftp.disconnect();return result;}ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录FTPFile[] fs = ftp.listFiles();for (FTPFile ff : fs) {if (ff.getName().equals(fileName)) {File localFile = new File(localPath + "/" + ff.getName());OutputStream is = new FileOutputStream(localFile);ftp.retrieveFile(ff.getName(), is);is.close();}}ftp.logout();result = true;} catch (IOException e) {e.printStackTrace();} finally {if (ftp.isConnected()) {try {ftp.disconnect();} catch (IOException ioe) {}}}return result;}public static void main(String[] args) {try {System.out.println("111");FileInputStream in=new FileInputStream(new File("D:/a.png"));System.out.println("222");boolean flag = uploadFile("192.168.75.128", 21, "ftpuser", "121891", "/home/ftpuser/","/", "abc1.png", in);  System.out.println(flag);  } catch (FileNotFoundException e) {System.out.println("error");e.printStackTrace();  }  }
}

​ 5.2 IDUtils

package com.ego.commons.utils;import java.util.Random;
import java.util.UUID;/*** 各种id生成策略* @version 1.0*/
public class IDUtils {/*** 图片名生成*/public static String genImageName() {//取当前时间的长整形值包含毫秒long millis = System.currentTimeMillis();//long millis = System.nanoTime();//加上三位随机数Random random = new Random();int end3 = random.nextInt(999);//如果不足三位前面补0String str = millis + String.format("%03d", end3);return str;}/*** 商品id生成*/public static long genItemId() {//取当前时间的长整形值包含毫秒long millis = System.currentTimeMillis();//long millis = System.nanoTime();//加上两位随机数Random random = new Random();int end2 = random.nextInt(99);//如果不足两位前面补0String str = millis + String.format("%02d", end2);long id = new Long(str);return id;}public static void main(String[] args) {for(int i=0;i< 100;i++)System.out.println(genItemId());}
}
  1. 在 ego-commons 中 src/main/resources 下新建 commons.properties

    ftpclient.host = 192.168.249.129
    ftpclient.port = 21
    ftpclient.username = ftpuser
    ftpclient.password = ftpuser
    ftpclient.basepath = /home/ftpuser
    ftpclient.filepath = /
    
  2. 在 ego-manage 的 applicationContext-spring 中添加属性文件扫描

    <!-- 扫描属性文件 -->
    <context:property-placeholder location="classpath:commons.properties"/>
    
  3. 在 ego-manage 中 PicService 接口及实现类
    8.1 方法返回值是 Map 原因

    ​ 8.1.1 jsp中使用 kindeditor 的多文件上传插件,要求返回值为固定格式,成功和失败返回值包含内容不一样,不建议使用新建一个类方式

    public interface PicService {/*** 文件上传* @param file* @return*/Map<String,Object> upload(MultipartFile file) throws IOException;
    }
    
    @Service
    public class PicServiceImpl implements PicService {@Value("${ftpclient.host}")private String host;@Value("${ftpclient.port}")private int port;@Value("${ftpclient.username}")private String username;@Value("${ftpclient.password}")private String password;@Value("${ftpclient.basepath}")private String basePath;@Value("${ftpclient.filepath}")private String filePath;@Overridepublic Map<String,Object> upload(MultipartFile file) throws IOException {String genImageName = IDUtils.genImageName()+file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));boolean result = FtpUtil.uploadFile(host, port, username, password, basePath, filePath, genImageName, file.getInputStream());Map<String,Object> map = new HashMap<>();if(result){map.put("error", 0);map.put("url","http://"+host+"/"+genImageName);}else{map.put("error", 1);map.put("message", "图片上传失败");} return map;}
    }
    
    1. 在 ego-manage 中新建 PicController

      @Controller
      public class PicController {@Resourceprivate PicService picServiceImpl;/*** 图片上传* @param uploadFile* @return*/@RequestMapping("pic/upload")@ResponseBodypublic Map<String,Object> upload(MultipartFile uploadFile){Map<String,Object> map = new HashMap<>();try {map= picServiceImpl.upload(uploadFile);} catch (IOException e) {e.printStackTrace();map.put("error", 1);map.put("message","上传图片时服务器异常");} return map;}
      }
      

jsonp跨域请求

  1. jsonp:跨域 ajax 数据请求的解决方案

  2. 发展由来
    2.1 ajax 不能进行跨域请求(如果 ajax 请求的控制器返回的就是字符串流或 json 数据,不能访问)
    2.2 发现可以在一个项目直接访问另一个项目的 js 文件.(标签引用还是通过 ajax 访问)
    2.3 使用 ajax 访问另一个项目的控制器,但是控制器返回的结果伪装成 js 文件.

  3. 使用 jsonp 时服务器端返回的数据格式: 函数名(返回的数据);

  4. 在客户端编写代码
    4.1 dataType: 一定要设置 jsonp
    4.2 jsonp: 传递给服务器的参数名.省略的默认 callback
    4.3 jsonCallback: 参数名对应的值,表示最终回调的函数名.省略的默认值 jquery:一堆数字
    4.4 如果直接使用 success:function()对 jsonpCallback 值没有要求.
    4.5 如果单独编写了一个 function,必须要求 jsonpCallback 和function 的名称相同

    $(function(){$("button").click(function(){$.ajax({url:'http://localhost:9002/demo2',type:'post',dataType:'jsonp',jsonpCallback:'ab12312321c',jsonp:'callback',success:function(data){alert(data+"success");}});})
    })
    
  5. 在服务器端添加代码
    5.1 使用 spring 对 jackson 封装类 MappingJacksonValue

    @Controller
    public class DemoController {@RequestMapping("demo3")@ResponseBodypublic MappingJacksonValue demo(String callback){People p = new People();p.setId(1);p.setName("张三");//把构造方法参数转换为 json 字符串并当作最终返回值函数的参数MappingJacksonValue mjv = new MappingJacksonValue(p);//最终返回结果中函数名mjv.setJsonpFunction(callback);return mjv;}
    }
    

Redis缓存

一.Redis 简介
  1. Redis 解释:Redis 是一个基于 key-value 形式进行存储的内存型数据库.
    1.1 数据存储方式为 key-value
    1.2 数据存储在内存中.
    1.2.1 优点:效率高.理论值:每秒 10K 数据读取.
    1.3 定位:数据库软件.
    1.3.1 作用:存储数据.

  2. Reids 是一个 NoSql 数据库.
    2.1 字面理解: 不使用 SQL 命令操作数据库软件.
    2.2 NoSQL : 英文全称 Not Only SQL ,表示在应用程序开发时,不是
    必须使用关系型数据库,可以使用 NoSQl 替代关系型数据库的部分
    功能.
    2.3 目前 NoSQL 不能完全替代关系型数据库.使用关系型数据库结
    合 NoSQl 数据库进行完成项目

    ​ 2.3.1 当数据比较复杂时不适用于 NoSQL 数据库
    ​ 2.3.2 关系型数据库依然做为数据存储的主要软件.
    ​ 2.3.3 NoSQL 数据库当作缓存工具来使用.
    ​ 2.3.3.1 把某些使用频率较高的内容不仅仅存储到关系型数据库中还存储到 NoSQL 数据中
    ​ 2.3.3.2 考虑到: NoSQL 和关系型数据库数据同步的问题

  3. Redis 持久化策略
    3.1 rdb
    3.1.1 默认的持久化策略.
    3.1.2 每隔一定时间后把内存中数据持久化到 dump.rdb 文件中.
    3.1.3 缺点:
    3.1.3.1 数据过于集中.
    3.1.3.2 可能导致最后的数据没有持久化到 dump.rdb 中
    3.1.3.2.1 解决办法:使用命令:SAVE 或 BGSAVE 手动持久化.
    3.2 aof
    3.2.1 监听 Redis 的日志文件,监听如果发现执行了修改,删除,新增命令.立即根据这条命令把数据持久化.
    3.2.2 缺点:
    3.2.2.1 效率降低

二. Redis 常用命令
  1. 命令手册网址 : http://doc.redisfans.com/

  2. Redis 数据类型
    2.1 String
    2.2 Hash
    2.3 List
    2.4 Set
    2.5 SortedSet 有序集合

三. Redis 几个常用概念
  1. Redis 默认有 16384 solts(槽),每个槽可以存储多个 hash 值.

  2. Redis 默认不需要密码:
    2.1 注释去掉,设置自己的密码

  1. 设置密码后需要通过
    3.1 -h 主机 ip
    3.2 -p 端口
    3.3 -a 密码

    ./redis-cli -h 192.168.139.132 -p 6379 -a smallming
    
四 . Jedis
  1. Jedis 是 Redis 客户端工具 jar

  2. 使用非集群版示例代码

    Jedis jedis = new Jedis("192.168.139.132", 6379);
    // 新增或修改
    // String result = jedis.set("address", "海淀");
    // 查询
    // String result1 = jedis.get("address");
    // 删除// Long index = jedis.del("address");
    
五. Jedis 集群
  1. 集群的概念:
    1.1 多个业务单元协同工作组成的整体称为集群.
    1.1.1 每个业务单元都是相同的.

  2. 当集群中业务单元中超过或等于 1/2 个 down 掉时整个集群不可用.
    2.1 建议使用奇数个,整体 down 机率小

  3. 一主一备模式
    3.1 给每个业务单元创建一个备份业务单元. 原来的业务单元(master)后产生的叫做(slave)

  4. 集群和伪集群
    4.1 集群: 每个业务单元都安装到单独的服务器上
    4.2 伪集群: 所有业务单元都安装到同一个服务器上,通过端口区分不同的业务单元.

  5. 使用 Jedis 访问 Reids 集群

    Set<HostAndPort> set = new HashSet<>();
    set.add(new HostAndPort("192.168.139.132",7001));
    set.add(new HostAndPort("192.168.139.132",7002));
    set.add(new HostAndPort("192.168.139.132",7003));
    set.add(new HostAndPort("192.168.139.132",7004));
    set.add(new HostAndPort("192.168.139.132",7005));
    set.add(new HostAndPort("192.168.139.132",7006));
    JedisCluster cluster = new JedisCluster(set);
    String result = cluster.get("a");
    System.out.println(result);
    
六. redis应用实例
  1. 新建 ego-redis 项目

  2. 在 ego-redis 的 pom.xml 中添加

    <dependencies><!-- jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><!-- spring --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId></dependency>
    </dependencies>
    
  3. 在 ego-redis 中添加 applicationContext-redis.xml,除了原有内容添加
    注解扫描

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"><context:component-scan base-package="com.ego.redis.dao.impl"></context:component-scan><!-- 配置jedis连接池 --><bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"><!-- 最大连接数 --><property name="maxTotal" value="30" /><!-- 最大空闲连接数 --><property name="maxIdle" value="10" /><!-- 每次释放连接的最大数目 --><property name="numTestsPerEvictionRun" value="1024" /><!-- 释放连接的扫描间隔(毫秒) --><property name="timeBetweenEvictionRunsMillis" value="30000" /><!-- 连接最小空闲时间 --><property name="minEvictableIdleTimeMillis" value="1800000" /><!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 --><property name="softMinEvictableIdleTimeMillis" value="10000" /><!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 --><property name="maxWaitMillis" value="1500" /><!-- 在获取连接的时候检查有效性, 默认false --><property name="testOnBorrow" value="true" /><!-- 在空闲时检查有效性, 默认false --><property name="testWhileIdle" value="true" /><!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true --><property name="blockWhenExhausted" value="false" /></bean><!-- jedisCluster --><bean id="jedisClients" class="redis.clients.jedis.JedisCluster"><constructor-arg name="poolConfig" ref="jedisPoolConfig"/><constructor-arg name="nodes"><set><bean class="redis.clients.jedis.HostAndPort"><constructor-arg name="host" value="192.168.43.114"/><constructor-arg name="port" value="7001"/></bean><bean class="redis.clients.jedis.HostAndPort"><constructor-arg name="host" value="192.168.43.114"/><constructor-arg name="port" value="7002"/></bean><bean class="redis.clients.jedis.HostAndPort"><constructor-arg name="host" value="192.168.43.114"/><constructor-arg name="port" value="7003"/></bean><bean class="redis.clients.jedis.HostAndPort"><constructor-arg name="host" value="192.168.43.114"/><constructor-arg name="port" value="7004"/></bean><bean class="redis.clients.jedis.HostAndPort"><constructor-arg name="host" value="192.168.43.114"/><constructor-arg name="port" value="7005"/></bean><bean class="redis.clients.jedis.HostAndPort"><constructor-arg name="host" value="192.168.43.114"/><constructor-arg name="port" value="7006"/></bean></set></constructor-arg></bean>
    </beans>
    
  4. 在 ego-redis 中添加 redis.properties

    redis.bigpic.key = bigpic
    
  5. 在 ego-redis 中新建 com.ego.redis.dao.JedisDao 和实现类

    public interface JedisDao {/*** 判断 key 是否存在* @param key* @return*/Boolean exists(String key);/*** 删除* @param key* @return*/Long del(String key);/*** 设置值* @param key* @param value* @return*/String set(String key,String value);/*** 取值* @param key* @return*/String get(String key);
    }
    
    @Repository
    public class JedisDaoImpl implements JedisDao{@Resourceprivate JedisCluster jedisClients;@Overridepublic Boolean exists(String key) {return jedisClients.exists(key);} @Overridepublic Long del(String key) {return jedisClients.del(key);} @Overridepublic String set(String key, String value) {return jedisClients.set(key, value);} @Overridepublic String get(String key) {return jedisClients.get(key);}
    }
    

Junit 4

  1. 单元测试插件.

  2. 使用 Junit 主要目的
    2.1 可以不用编写 main 方法

  3. 要求:
    3.1 方法必须是 public void 且没有参数
    3.2 当前项目不要有 Test 否则@Test 引用自己 Test 类

  4. 实现步骤:(Maven)
    4.1 在 pom.xml 中依赖 junit4

    <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version>
    </dependency>
    

    4.2 在需要测试的方法上添加@Test
    4.2.1 @Before 在@Test 之前执行
    4.2.2 @After 在@Test 之后执行.
    4.2.3 如果有多个@Test 每个@Test 前后都会执行@Before 和@After

    @Test
    public void testInsert() {}
    

Solr

一. 简介

  1. Solr 是什么?
    1.1 就是一个 war 项目

  2. 自己的项目如何和 Solr 进行交互?
    2.1 特定的 API 叫做 SolrJ

  3. 具备数据持久化功能.
    3.1 Solr 中会存储需要进行搜索的数据
    3.2 把所有数据都初始化到 Solr 中.

  4. Solr 作用(什么时候使用 Solr)
    4.1 大量数据检索时使用 Solr,能提升检索效率.

  5. Solr 是基于索引进行查询的.
    5.1 顺序查询:从内容的最开始找到内容为止
    5.2 反向键索引:

    ​ 5.2.1 把内容中进行拆分.

  6. 国内实现检索的常用方案.
    6.1 apache lucene : 实现检索的解决方案(Solr 基于 lucene)
    6.2 Baidu API:
    6.3 Google API

二. IK Analyzer 中文拆词器

  1. Solr 默认对中文拆词功能支持不好.
    1.1 解决方案:使用 IK Analyzer 拆词器

  2. 配置 IK Analyzer 时本质实际上是给 solr 新建了一个 filedType,只要
    某个属性(field)是这个类型,这个属性就会使用 IK Analyzer 进行拆词

三. Solr 管理界面的 documents 菜单功能

  1. 包含了 solr 数据的新增,删除,修改三个功能.

  2. 数据支持很多种格式:json 或 xml 等

  3. 新增时,必须包含对 id 的新增.

  4. 每次新增,Solr 会新建一个 SolrDocument 对象.这个对象存储新增内容.
    4.1 把 SolrDocument 理解成实体类.
    4.2 包含了 Solr 的 schema.xml 中配置的所有 field
    4.3 新增时有什么属性,最终显示时就只有什么属性.

  5. 把 DocumentType 以 xml 方式举例
    5.1 新增

    <doc><field name="id">change.me</field><field name="title">change.me</field>
    </doc>
    

    5.2 修改: 只要 id 已经存在执行修改
    5.3 删除:
    5.3.1 根据 id 删除

    <delete><id>id 内容</id>
    <delete>
    <commit/>
    

    ​ 5.3.2 删除全部

    <delete><query>*:*</query>
    </delete>
    <commit/>
    

四. 管理界面 quey 菜单功能

  1. q 表示查询条件
    1.1 : 查询全部
    1.2 bjsxt:java 培训
    1.2.1 把 java 培训拆分成 java 和培训,按照 java 和培训在 bjsxt属性中进行搜索.
    1.3 bjsxt:”java 培训”: java 培训不回被拆词
    1.4 在 schema.xml中配置复合属性,这个复合属性可以包含多个其他属性的值.
    1.4.1 在 schema.xml 添加下面内容

    <field name="bjsxtall" type="text_ik" indexed="true" stored="true" multiValued="true"/>
    <copyField source="bjsxt" dest="bjsxtall"/>
    <copyField source="bjsxt1" dest="bjsxtall"/>
    

五. SolrCloud

  1. solr 是 web 项目,需要放入到 tomcat 中.依赖多个 tomcat,让多个tomcat 之间能够通信.

  2. 需要借助 zookeeper 实现 tomcat 直接通信

  3. 结构图

六. 应用实例

一.商品搜索功能业务分析
  1. 搜索功能写在 ego-search 中

  2. 在编写 ego-search 项目的控制器时发送给 jsp 多个值
    2.1 ${query} 搜索内容
    2.2 ${itemList} 搜索到商品列表
    2.2.1 商品的图片是数组类型
    2.3 ${totalPages} 总页数
    2.4 ${page} 当前页
    \3. Solr 完成步骤:
    3.1 先配置 field
    3.1.1 注意:field 的名称不要和原有名称重复.
    3.2 数据初始化.

    3.3 完成搜索功能

二. 配置 Solr 的 Field
  1. 已经在 Solr 中安装好 IK Analyzer

  2. 在 Solrhome/collection1/conf/schema.xml 中配置配置业务字段

    <field name="item_title" type="text_ik" indexed="true" stored="true"/>
    <field name="item_sell_point" type="text_ik" indexed="true" stored="true"/>
    <field name="item_price" type="long" indexed="true" stored="true"/>
    <field name="item_image" type="string" indexed="false" stored="true"/>
    <field name="item_category_name" type="string" indexed="true" stored="true" />
    <field name="item_desc" type="text_ik" indexed="true" stored="false"/>
    <field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
    <copyField source="item_title" dest="item_keywords"/>
    <copyField source="item_sell_point" dest="item_keywords"/>
    <copyField source="item_category_name" dest="item_keywords"/>
    <copyField source="item_desc" dest="item_keywords"/>
    
三. 创建 ego-search 项目
  1. 新建 ego-search

  2. 在 ego-parent 中添加

    <dependency><groupId>org.apache.solr</groupId><artifactId>solr-solrj</artifactId><version>5.3.1</version>
    </dependency>
    
四.Solr 数据初始化
  1. 由于 ego-manage 没有提供 solr 初始化按钮等功能.
    1.1 只能在 ego-search 编写控制器,通过浏览器访问实现数据初始化.

  2. 在 dubbo 中 consumer 调用 privider 默认最大数据传输量为 8M
    2.1 dubbo:protocal/中 payload=”8388608” 取值为 int,表示每次consumer 向 provider 请求数据时数据响应最大量.单位字节

  3. 实现步骤:
    3.1 在 ego-service 中 TbItemDubboService 及实现类添加

    /**
    * 通过状态查询全部可用数据
    * @return
    */
    List<TbItem> selAllByStatus(byte status);================================================================@Override
    public List<TbItem> selAllByStatus(byte status) {TbItemExample example = new TbItemExample();example.createCriteria().andStatusEqualTo(status);return tbItemMapper.selectByExample(example);
    }
    

    3.2 在 ego-service 中 TbItemDescDubboService 及实现类添加

    /**
    * 根据主键查询商品描述对象
    * @param itemid
    * @return
    */
    TbItemDesc selByItemid(long itemid);
    @Override
    public TbItemDesc selByItemid(long itemid) {return tbItemDescMapper.selectByPrimaryKey(itemid);
    }
    

    3.3 在 ego-search 中新建 applicationContext-solr.xml

    <bean id="solrClient" class="org.apache.solr.client.solrj.impl.CloudSolrClient"><constructor-arg type="java.lang.String" value="192.168.249.131:2181,192.168.249.131:2182,192.168.249.131:2183"></constructor-arg>
    <property name="defaultCollection"value="collection1"></property>
    </bean>
    

    3.4 在 ego-search 中新建 com.ego.search.service.TbItemService 及实现类

    public interface TbItemService {void init() throws SolrServerException, IOException;
    } ==============================================================@Service
    public class TbItemServiceImpl implements TbItemService{@Referenceprivate TbItemDubboService tbItemDubboServiceImpl;@Referenceprivate TbItemCatDubboService tbItemCatDubboServiceImpl;@Referenceprivate TbItemDescDubboService tbItemDescDubboServiceImpl;@Resourceprivate CloudSolrClient solrClient;@Overridepublic void init() throws SolrServerException, IOException {//查询所有正常的商品List<TbItem> listItem = tbItemDubboServiceImpl.selAllByStatus((byte)1);for (TbItem item : listItem) {//商品对应的类目信息TbItemCat cat = tbItemCatDubboServiceImpl.selById(item.getCid());//商品对应的描述信息TbItemDesc desc =tbItemDescDubboServiceImpl.selByItemid(item.getId());SolrInputDocument doc = new SolrInputDocument();doc.addField("id", item.getId());doc.addField("item_title", item.getTitle());doc.addField("item_sell_point", item.getSellPoint());doc.addField("item_price",item.getPrice() );doc.addField("item_image", item.getImage());doc.addField("item_category_name", cat.getName());doc.addField("item_desc", desc.getItemDesc());solrClient.add(doc);} solrClient.commit();}
    }
    

    3.5 在 ego-search 中 新 建 控 制 器com.ego.search.controller.TbItemController

@Controller
public class TbItemController {@Resourceprivate TbItemService tbItemServiceImpl;/*** 初始化* @return*/@RequestMapping(value="solr/init",produces="text/html;charset=utf-8")@ResponseBodypublic String init(){long start = System.currentTimeMillis();try {tbItemServiceImpl.init();long end = System.currentTimeMillis();return "初始化总时间:"+(end-start)/1000+"秒";} catch (Exception e) {e.printStackTrace();return "初始化失败";}}
}

五. 门户搜索功能

  1. 在 ego-search 新建实体类 com.ego.search.pojo.TbItemChild

    public class TbItemChild extends TbItem {private String [] images;
    }
    
  2. 在 ego-search 的 TbItemService 及实现类添加

    /**
    * 分页查询
    * @param query
    * @return
    */
    Map<String,Object> selByQuery(String query,intpage,int rows) throws SolrServerException, IOException;================================================================@Override
    public Map<String, Object> selByQuery(String query,int page,int rows) throws SolrServerException,IOException {SolrQuery params = new SolrQuery();//设置分页条件params.setStart(rows*(page-1));params.setRows(rows);//设置条件params.setQuery("item_keywords:"+query);//设置高亮params.setHighlight(true);params.addHighlightField("item_title");params.setHighlightSimplePre("<span style='color:red'>");params.setHighlightSimplePost("</span>");QueryResponse response = solrClient.query(params);List<TbItemChild> listChild = new ArrayList<>();//未高亮内容SolrDocumentList listSolr = response.getResults();//高亮内容Map<String, Map<String, List<String>>> hh =response.getHighlighting();for (SolrDocument doc : listSolr) {TbItemChild child = new TbItemChild();child.setId(Long.parseLong(doc.getFieldValue("id").toString()));List<String> list =hh.get(doc.getFieldValue("id")).get("item_title");if(list!=null&&list.size()>0){child.setTitle(list.get(0));}else{child.setTitle(doc.getFieldValue("item_title").toString());}child.setPrice((Long)doc.getFieldValue("item_price"));Object image = doc.getFieldValue("item_image");child.setImages(image==null||image.equals("")?newString[1]:image.toString().split(","));child.setSellPoint(doc.getFieldValue("item_sell_point").toString());listChild.add(child);} Map<String,Object> resultMap = new HashMap<>();resultMap.put("itemList", listChild);resultMap.put("totalPages",listSolr.getNumFound()%rows==0?listSolr.getNumFound()/rows:listSolr.getNumFound()/rows+1);return resultMap;
    }
    
  3. 在 ego-search 中 TbItemController 编写控制器

    /**
    * 搜索功能
    * @param model
    * @param q
    * @param page
    * @param rows
    * @return
    */
    @RequestMapping("search.html")
    public String search(Model model,String q, @RequestParam(defaultValue="1") int page, @RequestParam(defaultValue="12") int rows){try {q = newString(q.getBytes("iso-8859-1"),"utf-8");Map<String, Object> map =tbItemServiceImpl.selByQuery(q, page, rows);model.addAttribute("query", q);model.addAttribute("itemList",map.get("itemList"));model.addAttribute("totalPages",map.get("totalPages"));model.addAttribute("page", page);} catch (Exception e) {e.printStackTrace();} return "search";
    }
    

HttpClient

HttpClient 简介

  1. 由 apache 推出的实现使用 java 代码完成请求/响应的一套 API

  2. 实现效果:
    2.1 模拟浏览器发送请求及解析响应内容

  3. 常用类:
    3.1 CloseableHttpClient :负责发送请求和接收响应.相当于浏览器
    3.2 HttpPost: 请求对象,所有请求信息都放入到这个对象中

    ​ 3.2.1 HttpGet: get 方式的请求
    3.3 CloseableHttpResponse: 响应对象,所有响应信息放入到这个类
    3.4 EntityUtils: 工具类,解析响应体

示例代码:

  • 使用httpclient执行get请求

    @Testpublic void doGet() throws Exception {//创建一个httpclient对象CloseableHttpClient httpClient = HttpClients.createDefault();//创建一个GET对象HttpGet get = new HttpGet("http://www.sogou.com");//执行请求CloseableHttpResponse response = httpClient.execute(get);//取响应的结果int statusCode = response.getStatusLine().getStatusCode();System.out.println(statusCode);HttpEntity entity = response.getEntity();String string = EntityUtils.toString(entity, "utf-8");System.out.println(string);//关闭httpclientresponse.close();httpClient.close();}
  • 执行get请求带参数

    @Testpublic void doGetWithParam() throws Exception{//创建一个httpclient对象CloseableHttpClient httpClient = HttpClients.createDefault();//创建一个uri对象URIBuilder uriBuilder = new URIBuilder("http://www.sogou.com/web");uriBuilder.addParameter("query", "花千骨");HttpGet get = new HttpGet(uriBuilder.build());//执行请求CloseableHttpResponse response = httpClient.execute(get);//取响应的结果int statusCode = response.getStatusLine().getStatusCode();System.out.println(statusCode);HttpEntity entity = response.getEntity();String string = EntityUtils.toString(entity, "utf-8");System.out.println(string);//关闭httpclientresponse.close();httpClient.close();}
  • 使用httpclient执行post请求

    @Testpublic void doPost() throws Exception {CloseableHttpClient httpClient = HttpClients.createDefault();//创建一个post对象HttpPost post = new HttpPost("http://localhost:8082/httpclient/post.html");//执行post请求CloseableHttpResponse response = httpClient.execute(post);String string = EntityUtils.toString(response.getEntity());System.out.println(string);response.close();httpClient.close();}
  • 带参数post请求

       @Testpublic void doPostWithParam() throws Exception{CloseableHttpClient httpClient = HttpClients.createDefault();//创建一个post对象HttpPost post = new HttpPost("http://localhost:8082/httpclient/post.html");//创建一个Entity。模拟一个表单List<NameValuePair> kvList = new ArrayList<>();kvList.add(new BasicNameValuePair("username", "zhangsan"));kvList.add(new BasicNameValuePair("password", "123"));//包装成一个Entity对象StringEntity entity = new UrlEncodedFormEntity(kvList, "utf-8");//设置请求的内容post.setEntity(entity);//执行post请求CloseableHttpResponse response = httpClient.execute(post);String string = EntityUtils.toString(response.getEntity());System.out.println(string);response.close();httpClient.close();}

HttpCilentUtils工具类

package com.ego.commons.utils;import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;public class HttpClientUtil {public static String doGet(String url, Map<String, String> param) {// 创建Httpclient对象CloseableHttpClient httpclient = HttpClients.createDefault();String resultString = "";CloseableHttpResponse response = null;try {// 创建uriURIBuilder builder = new URIBuilder(url);if (param != null) {for (String key : param.keySet()) {builder.addParameter(key, param.get(key));}}URI uri = builder.build();// 创建http GET请求HttpGet httpGet = new HttpGet(uri);// 执行请求response = httpclient.execute(httpGet);// 判断返回状态是否为200if (response.getStatusLine().getStatusCode() == 200) {resultString = EntityUtils.toString(response.getEntity(), "UTF-8");}} catch (Exception e) {e.printStackTrace();} finally {try {if (response != null) {response.close();}httpclient.close();} catch (IOException e) {e.printStackTrace();}}return resultString;}public static String doGet(String url) {return doGet(url, null);}public static String doPost(String url, Map<String, String> param) {// 创建Httpclient对象CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = null;String resultString = "";try {// 创建Http Post请求HttpPost httpPost = new HttpPost(url);// 创建参数列表if (param != null) {List<NameValuePair> paramList = new ArrayList<>();for (String key : param.keySet()) {paramList.add(new BasicNameValuePair(key, param.get(key)));}// 模拟表单UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8");httpPost.setEntity(entity);}// 执行http请求response = httpClient.execute(httpPost);resultString = EntityUtils.toString(response.getEntity(), "utf-8");} catch (Exception e) {e.printStackTrace();} finally {try {response.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return resultString;}public static String doPost(String url) {return doPost(url, null);}public static String doPostJson(String url, String json) {// 创建Httpclient对象CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = null;String resultString = "";try {// 创建Http Post请求HttpPost httpPost = new HttpPost(url);// 创建请求内容StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);httpPost.setEntity(entity);// 执行http请求response = httpClient.execute(httpPost);resultString = EntityUtils.toString(response.getEntity(), "utf-8");} catch (Exception e) {e.printStackTrace();} finally {try {response.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return resultString;}
}

SSO(单点登录)

一. SSO 简介

  1. SSO 英文名称(Single Sign On)

  2. SSO 中文名称:单点登录

  3. 单点登录解释:
    3.1 一次登录,让其他项目共享登录状态.
    3.2 本质:使用特定技术在分布式项目中模拟 HttpSession 功能.

    ​ 3.2.1 技术选型:Redis+Cookie 模拟 HttpSession 功能

  4. 传统项目和分布式项目登录功能对比
    4.1 传统项目

​ 4.2 分布式项目

二. Cookie 复习

  1. 解释:客户端存值技术.
    1.1 存储位置:客户端浏览器.
    1.2 作用:存值
    1.3 存值类型: 只能存储字符串.

  2. Cookie 运行原理:
    2.1 当浏览器输入 URL 访问服务器时会自动携带所有有效
    Cookie(时间内,指定路径内,指定域名内),Tomcat 接收请求后会把
    Cookie 放入到 HttpServletRequest 中,在代码中通过 request 对象获
    取到 Cookie 的内容.
    2.2 服务器内容可以产生 Cookie 内容,需要放入到响应对象中响应
    给客户端浏览器.(要求跳转类型为重定向.),客户端浏览器接收响
    应内容后会把 Cookie 内容存储到指定文件夹内容.

  3. 产生 cookie

    @RequestMapping("demo")
    public String goIndex(HttpServletResponse response){Cookie c = new Cookie("key", "value");//1. 默认实现和 HttpSession 相同.//设置 cookie 存活时间,单位秒// c.setMaxAge(10);//2. 默认存储路径为 path=///设置哪个目录下资源能访问c.setPath("/");//3. 域名和当前项目的域名相同c.setDomain(".bjsxt.com");response.addCookie(c);return "redirect:/index.jsp";
    }
    
  4. 获取 cookie

    <%Cookie [] cs = request.getCookies();if(cs!=null){for(Cookie c :cs){out.print("key:"+c.getName()+",value:"+c.getValue() +"<br/>");}}else{out.println("没有 cookie");}
    %>
    
  5. cookieUitls

    package com.ego.commons.utils;import java.io.UnsupportedEncodingException;
    import java.net.URLDecoder;
    import java.net.URLEncoder;import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;/*** * Cookie 工具类**/
    public final class CookieUtils {/*** 得到Cookie的值, 不编码* * @param request* @param cookieName* @return*/public static String getCookieValue(HttpServletRequest request, String cookieName) {return getCookieValue(request, cookieName, false);}/*** 得到Cookie的值,* * @param request* @param cookieName* @return*/public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {Cookie[] cookieList = request.getCookies();if (cookieList == null || cookieName == null) {return null;}String retValue = null;try {for (int i = 0; i < cookieList.length; i++) {if (cookieList[i].getName().equals(cookieName)) {if (isDecoder) {retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");} else {retValue = cookieList[i].getValue();}break;}}} catch (UnsupportedEncodingException e) {e.printStackTrace();}return retValue;}/*** 得到Cookie的值,* * @param request* @param cookieName* @return*/public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {Cookie[] cookieList = request.getCookies();if (cookieList == null || cookieName == null) {return null;}String retValue = null;try {for (int i = 0; i < cookieList.length; i++) {if (cookieList[i].getName().equals(cookieName)) {retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);break;}}} catch (UnsupportedEncodingException e) {e.printStackTrace();}return retValue;}/*** 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue) {setCookie(request, response, cookieName, cookieValue, -1);}/*** 设置Cookie的值 在指定时间内生效,但不编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage) {setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);}/*** 设置Cookie的值 不设置生效时间,但编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, boolean isEncode) {setCookie(request, response, cookieName, cookieValue, -1, isEncode);}/*** 设置Cookie的值 在指定时间内生效, 编码参数*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage, boolean isEncode) {doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);}/*** 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage, String encodeString) {doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);}/*** 删除Cookie带cookie域名*/public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,String cookieName) {doSetCookie(request, response, cookieName, "", -1, false);}/*** 设置Cookie的值,并使其在指定时间内生效* * @param cookieMaxage cookie生效的最大秒数*/private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {try {if (cookieValue == null) {cookieValue = "";} else if (isEncode) {cookieValue = URLEncoder.encode(cookieValue, "utf-8");}Cookie cookie = new Cookie(cookieName, cookieValue);if (cookieMaxage > 0)cookie.setMaxAge(cookieMaxage);if (null != request) {// 设置域名的cookieString domainName = getDomainName(request);System.out.println(domainName);
    //                if (!"localhost".equals(domainName)) {//                  cookie.setDomain(domainName);
    //                }}cookie.setPath("/");response.addCookie(cookie);} catch (Exception e) {e.printStackTrace();}}/*** 设置Cookie的值,并使其在指定时间内生效* * @param cookieMaxage cookie生效的最大秒数*/private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,String cookieName, String cookieValue, int cookieMaxage, String encodeString) {try {if (cookieValue == null) {cookieValue = "";} else {cookieValue = URLEncoder.encode(cookieValue, encodeString);}Cookie cookie = new Cookie(cookieName, cookieValue);if (cookieMaxage > 0)cookie.setMaxAge(cookieMaxage);if (null != request) {// 设置域名的cookieString domainName = getDomainName(request);System.out.println(domainName);if (!"localhost".equals(domainName)) {cookie.setDomain(domainName);}}cookie.setPath("/");response.addCookie(cookie);} catch (Exception e) {e.printStackTrace();}}/*** 得到cookie的域名*/private static final String getDomainName(HttpServletRequest request) {String domainName = null;String serverName = request.getRequestURL().toString();if (serverName == null || serverName.equals("")) {domainName = "";} else {final int end = serverName.indexOf("/");serverName = serverName.substring(0, end);final String[] domains = serverName.split("\\.");int len = domains.length;if (len > 3) {// www.xxx.com.cndomainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];} else if (len <= 3 && len > 1) {// xxx.com or xxx.cndomainName = "." + domains[len - 2] + "." + domains[len - 1];} else {domainName = serverName;}}if (domainName != null && domainName.indexOf(":") > 0) {String[] ary = domainName.split("\\:");domainName = ary[0];}return domainName;}}

三. HttpSession 运行原理.

  1. 当客户端浏览器访问服务器时,服务器接收请求后会判断请求中是
    否在 Cookie 包含 JSESSIONID.

  2. 如 果 包 含 JSESSIONID 把 对 应 值 取 出 , 做 为 Key 从 全 局
    Map<String,Session>对象往出取 Session 对象.

  3. 如果有对应的 key,把 Session 对象取出.根据自己业务添加操作.

  4. 如果请求对象中 JSESSIONID 在全局 Map<String,Session>没有或请
    求对象中 Cookie 没有 JSESSSIONID ,会执行新建 Session 步骤

  5. Tomcat 会新建(new)一个 Sesssion 对象,同时产生一个 UUID,把 UUID
    做为 Map 的 key,新建的 Session 对象做为 Value,还会把 UUID 放入到
    Cookie 做为 value,value 对应的 Key 是 JSESSIONID
    四使用 Redis+Cookie 模拟 Session 步骤:

  6. 流 程 图

  1. 步骤:
    2.1 步骤 1:第一次请求时 Cookie 中没有 token
    2.2 产生一个 UUID,把”token”做为 Cookie 的 key,UUID 做为 Cookie的 value
    2.3 如果希望类似往 Session 存储内容,直接把 UUID 当作 redis 的key(个别需要还需要考虑 key 重复问题.),把需要存储的内容做为value.
    2.4 把 cookie 对象响应给客户端浏览器.

MyCat

一.MySQL 主从简介

  1. MySQL 默认支持主(master)从(slave)功能.

  2. 配置完主从备份后效果:在主数据库中操作时,从同步进行变化.

  3. 主从本质:主数据的操作写入到日志中,从数据库从日志中读取,进行操作.

  4. 主从原理
    4.1 默认 MySQL 没有开启日志功能
    4.2 每个 数据库需 要有一 个 server_id, 主 server_id 值小 于从server_id
    4.3 每个 mysql 都有一个 uuid,由于虚拟机直接进行克隆,需要修改uuid 的值.
    4.4 必须要在主数据库中有一个用户具有被从数据库操作的权限

二. MyCat 简介

  1. 利用 mySQL 主从备份功能实现读写分离.
    1.1 增加,删除,修改,都操作主数据库
    1.2 查询到从数据库中查询.
    1.3 优点:提升程序执行性能.

  2. MyCat 解释: 数据库中间件软件.

  1. MyCat 具备分库/分表功能.
    3.1 默认 MyCat 分库(建议使用)
    3.2 可以配置让MyCat进行分表,业务比较复杂,配置起来也比较复杂.
  2. MyCat 中默认 tableRule 要求至少 3 个 database
  3. 必知的几个概念:
    5.1 逻辑库: 一个包含了所有数据库的逻辑上的数据库
    5.2 逻辑表: 一个包含了所有表的逻辑上的表
    5.3 数据主机:数据库软件安装到哪个服务器上
    5.4 数据节点:数据库软件中的 database
    5.5 分片规则: 默认每个表中数据都一样的
    5.6 读主机:哪个数据库做为读操作
    5.7 写主机.:哪个数据做为写操作

工具类汇总:

CookieUtils

package com.ego.commons.utils;import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/*** * Cookie 工具类**/
public final class CookieUtils {/*** 得到Cookie的值, 不编码* * @param request* @param cookieName* @return*/public static String getCookieValue(HttpServletRequest request, String cookieName) {return getCookieValue(request, cookieName, false);}/*** 得到Cookie的值,* * @param request* @param cookieName* @return*/public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {Cookie[] cookieList = request.getCookies();if (cookieList == null || cookieName == null) {return null;}String retValue = null;try {for (int i = 0; i < cookieList.length; i++) {if (cookieList[i].getName().equals(cookieName)) {if (isDecoder) {retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");} else {retValue = cookieList[i].getValue();}break;}}} catch (UnsupportedEncodingException e) {e.printStackTrace();}return retValue;}/*** 得到Cookie的值,* * @param request* @param cookieName* @return*/public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {Cookie[] cookieList = request.getCookies();if (cookieList == null || cookieName == null) {return null;}String retValue = null;try {for (int i = 0; i < cookieList.length; i++) {if (cookieList[i].getName().equals(cookieName)) {retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);break;}}} catch (UnsupportedEncodingException e) {e.printStackTrace();}return retValue;}/*** 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue) {setCookie(request, response, cookieName, cookieValue, -1);}/*** 设置Cookie的值 在指定时间内生效,但不编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage) {setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);}/*** 设置Cookie的值 不设置生效时间,但编码*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, boolean isEncode) {setCookie(request, response, cookieName, cookieValue, -1, isEncode);}/*** 设置Cookie的值 在指定时间内生效, 编码参数*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage, boolean isEncode) {doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);}/*** 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)*/public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,String cookieValue, int cookieMaxage, String encodeString) {doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);}/*** 删除Cookie带cookie域名*/public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,String cookieName) {doSetCookie(request, response, cookieName, "", -1, false);}/*** 设置Cookie的值,并使其在指定时间内生效* * @param cookieMaxage cookie生效的最大秒数*/private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {try {if (cookieValue == null) {cookieValue = "";} else if (isEncode) {cookieValue = URLEncoder.encode(cookieValue, "utf-8");}Cookie cookie = new Cookie(cookieName, cookieValue);if (cookieMaxage > 0)cookie.setMaxAge(cookieMaxage);if (null != request) {// 设置域名的cookieString domainName = getDomainName(request);System.out.println(domainName);
//                if (!"localhost".equals(domainName)) {//                  cookie.setDomain(domainName);
//                }}cookie.setPath("/");response.addCookie(cookie);} catch (Exception e) {e.printStackTrace();}}/*** 设置Cookie的值,并使其在指定时间内生效* * @param cookieMaxage cookie生效的最大秒数*/private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,String cookieName, String cookieValue, int cookieMaxage, String encodeString) {try {if (cookieValue == null) {cookieValue = "";} else {cookieValue = URLEncoder.encode(cookieValue, encodeString);}Cookie cookie = new Cookie(cookieName, cookieValue);if (cookieMaxage > 0)cookie.setMaxAge(cookieMaxage);if (null != request) {// 设置域名的cookieString domainName = getDomainName(request);System.out.println(domainName);if (!"localhost".equals(domainName)) {cookie.setDomain(domainName);}}cookie.setPath("/");response.addCookie(cookie);} catch (Exception e) {e.printStackTrace();}}/*** 得到cookie的域名*/private static final String getDomainName(HttpServletRequest request) {String domainName = null;String serverName = request.getRequestURL().toString();if (serverName == null || serverName.equals("")) {domainName = "";} else {final int end = serverName.indexOf("/");serverName = serverName.substring(0, end);final String[] domains = serverName.split("\\.");int len = domains.length;if (len > 3) {// www.xxx.com.cndomainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];} else if (len <= 3 && len > 1) {// xxx.com or xxx.cndomainName = "." + domains[len - 2] + "." + domains[len - 1];} else {domainName = serverName;}}if (domainName != null && domainName.indexOf(":") > 0) {String[] ary = domainName.split("\\:");domainName = ary[0];}return domainName;}}

FtpUtils

package com.ego.commons.utils;import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;/*** ftp上传下载工具类*/
public class FtpUtil {/** * Description: 向FTP服务器上传文件 * @param host FTP服务器hostname * @param port FTP服务器端口 * @param username FTP登录账号 * @param password FTP登录密码 * @param basePath FTP服务器基础目录* @param filePath FTP服务器文件存放路径。例如分日期存放:/2015/01/01。文件的路径为basePath+filePath* @param filename 上传到FTP服务器上的文件名 * @param input 输入流 * @return 成功返回true,否则返回false */
public static boolean uploadFile(String host, int port, String username, String password, String basePath,String filePath, String filename, InputStream input) {boolean result = false;FTPClient ftp = new FTPClient();try {int reply;ftp.connect(host, port);// 连接FTP服务器// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器ftp.login(username, password);// 登录reply = ftp.getReplyCode();if (!FTPReply.isPositiveCompletion(reply)) {ftp.disconnect();    return result;}//切换到上传目录if (!ftp.changeWorkingDirectory(basePath+filePath)) {//如果目录不存在创建目录String[] dirs = filePath.split("/");String tempPath = basePath;for (String dir : dirs) {if (null == dir || "".equals(dir)) continue;tempPath += "/" + dir;if (!ftp.changeWorkingDirectory(tempPath)) {if (!ftp.makeDirectory(tempPath)) {return result;} else {ftp.changeWorkingDirectory(tempPath);}}}}//设置上传文件的类型为二进制类型ftp.setFileType(FTP.BINARY_FILE_TYPE);//上传文件if (!ftp.storeFile(filename, input)) {System.out.println("second:" + result);return result;}input.close();ftp.logout();result = true;} catch (IOException e) {e.printStackTrace();} finally {if (ftp.isConnected()) {try {ftp.disconnect();} catch (IOException ioe) {}}}System.out.println("end:" + result);return result;}/** * Description: 从FTP服务器下载文件 * @param host FTP服务器hostname * @param port FTP服务器端口 * @param username FTP登录账号 * @param password FTP登录密码 * @param remotePath FTP服务器上的相对路径 * @param fileName 要下载的文件名 * @param localPath 下载后保存到本地的路径 * @return */  public static boolean downloadFile(String host, int port, String username, String password, String remotePath,String fileName, String localPath) {boolean result = false;FTPClient ftp = new FTPClient();try {int reply;ftp.connect(host, port);// 如果采用默认端口,可以使用ftp.connect(host)的方式直接连接FTP服务器ftp.login(username, password);// 登录reply = ftp.getReplyCode();if (!FTPReply.isPositiveCompletion(reply)) {ftp.disconnect();return result;}ftp.changeWorkingDirectory(remotePath);// 转移到FTP服务器目录FTPFile[] fs = ftp.listFiles();for (FTPFile ff : fs) {if (ff.getName().equals(fileName)) {File localFile = new File(localPath + "/" + ff.getName());OutputStream is = new FileOutputStream(localFile);ftp.retrieveFile(ff.getName(), is);is.close();}}ftp.logout();result = true;} catch (IOException e) {e.printStackTrace();} finally {if (ftp.isConnected()) {try {ftp.disconnect();} catch (IOException ioe) {}}}return result;}public static void main(String[] args) {try {System.out.println("111");FileInputStream in=new FileInputStream(new File("D:/a.png"));System.out.println("222");boolean flag = uploadFile("192.168.75.128", 21, "ftpuser", "121891", "/home/ftpuser/","/", "abc1.png", in);  System.out.println(flag);  } catch (FileNotFoundException e) {System.out.println("error");e.printStackTrace();  }  }
}

HttpClientUtils

package com.ego.commons.utils;import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;public class HttpClientUtil {public static String doGet(String url, Map<String, String> param) {// 创建Httpclient对象CloseableHttpClient httpclient = HttpClients.createDefault();String resultString = "";CloseableHttpResponse response = null;try {// 创建uriURIBuilder builder = new URIBuilder(url);if (param != null) {for (String key : param.keySet()) {builder.addParameter(key, param.get(key));}}URI uri = builder.build();// 创建http GET请求HttpGet httpGet = new HttpGet(uri);// 执行请求response = httpclient.execute(httpGet);// 判断返回状态是否为200if (response.getStatusLine().getStatusCode() == 200) {resultString = EntityUtils.toString(response.getEntity(), "UTF-8");}} catch (Exception e) {e.printStackTrace();} finally {try {if (response != null) {response.close();}httpclient.close();} catch (IOException e) {e.printStackTrace();}}return resultString;}public static String doGet(String url) {return doGet(url, null);}public static String doPost(String url, Map<String, String> param) {// 创建Httpclient对象CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = null;String resultString = "";try {// 创建Http Post请求HttpPost httpPost = new HttpPost(url);// 创建参数列表if (param != null) {List<NameValuePair> paramList = new ArrayList<NameValuePair>();for (String key : param.keySet()) {paramList.add(new BasicNameValuePair(key, param.get(key)));}// 模拟表单UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8");httpPost.setEntity(entity);}// 执行http请求response = httpClient.execute(httpPost);resultString = EntityUtils.toString(response.getEntity(), "utf-8");} catch (Exception e) {e.printStackTrace();} finally {try {response.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return resultString;}public static String doPost(String url) {return doPost(url, null);}public static String doPostJson(String url, String json) {// 创建Httpclient对象CloseableHttpClient httpClient = HttpClients.createDefault();CloseableHttpResponse response = null;String resultString = "";try {// 创建Http Post请求HttpPost httpPost = new HttpPost(url);// 创建请求内容StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);httpPost.setEntity(entity);// 执行http请求response = httpClient.execute(httpPost);resultString = EntityUtils.toString(response.getEntity(), "utf-8");} catch (Exception e) {e.printStackTrace();} finally {try {response.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return resultString;}
}

IDUtils

package com.ego.commons.utils;import java.util.Random;
import java.util.UUID;/*** 各种id生成策略* @version 1.0*/
public class IDUtils {/*** 图片名生成*/public static String genImageName() {//取当前时间的长整形值包含毫秒long millis = System.currentTimeMillis();//long millis = System.nanoTime();//加上三位随机数Random random = new Random();int end3 = random.nextInt(999);//如果不足三位前面补0String str = millis + String.format("%03d", end3);return str;}/*** 商品id生成*/public static long genItemId() {//取当前时间的长整形值包含毫秒long millis = System.currentTimeMillis();//long millis = System.nanoTime();//加上两位随机数Random random = new Random();int end2 = random.nextInt(99);//如果不足两位前面补0String str = millis + String.format("%02d", end2);long id = new Long(str);return id;}public static void main(String[] args) {for(int i=0;i< 100;i++)System.out.println(genItemId());}
}

JsonUtils

package com.ego.commons.utils;import java.util.List;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;/*** 易购商城自定义响应结构*/
public class JsonUtils {// 定义jackson对象private static final ObjectMapper MAPPER = new ObjectMapper();/*** 将对象转换成json字符串。* <p>Title: pojoToJson</p>* <p>Description: </p>* @param data* @return*/public static String objectToJson(Object data) {try {String string = MAPPER.writeValueAsString(data);return string;} catch (JsonProcessingException e) {e.printStackTrace();}return null;}/*** 将json结果集转化为对象* * @param jsonData json数据* @param clazz 对象中的object类型* @return*/public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {try {T t = MAPPER.readValue(jsonData, beanType);return t;} catch (Exception e) {e.printStackTrace();}return null;}/*** 将json数据转换成pojo对象list* <p>Title: jsonToList</p>* <p>Description: </p>* @param jsonData* @param beanType* @return*/public static <T>List<T> jsonToList(String jsonData, Class<T> beanType) {JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);try {List<T> list = MAPPER.readValue(jsonData, javaType);return list;} catch (Exception e) {e.printStackTrace();}return null;}}

项目部署

一. 部署 Dubbo

  1. 启动 zookeeper 所在的虚拟机

  2. 修改 rc.local 让 zookeeper 自启动
    2.1 jdk 必须配置vim /etc/rc.d/rc.local

  1. 对 dubbo-service-impl 使用 assembly 打包插件进行打包

  1. 把打包后的文件上传到新的虚拟机(192.168.249.135)
  2. 在新的虚拟机中配置 JDK 环境变量.
  3. 把上传的压缩包解压到/usr/local/ego-dubbo
  4. 进入 ego-dubbo/bin,通过 start.sh 启动 ./start.sh
  5. 放行防火墙 20888 端口

二. 设置 redis 开机自启动

  1. 修改/usr/local/redis-cluster/startup.sh,在最上面添加

    cd /usr/local/redis-cluster
    
  2. 在/etc/rc.local 修改成下面内容

三. 使用 Nginx 实现负载均衡

  1. 前提:希望降低 tomcat 负载.

  2. 解决方案
    2.1 使用多个 tomcat
    2.2 每个 tomcat 中都部署相同的项目
    2.3 使用 nginx 反向代理多个 tomcat

  3. 结构图

四.部署 ego-portal

  1. 克隆虚拟机,安装 JDK,tomcat

  2. 修改 tomcat 的 conf/tomcat-users.xml 实现热部署

  3. 发布项目到 tomcat 中

  4. 修改 tomcat 端口为 80

  5. 修改防火墙放行 80 端口

  6. 把新克隆的虚拟机(包含 tomcat 这个虚拟机)克隆一份

  7. 在克隆一个新的虚拟机(要什么也没有安装环境),并在上面安扎 un个 nginx

  8. 修改 nginx/conf/nginx.conf 配置如下
    8.1 upstream 配置负载均衡
    8.2 server nginx 代理的服务器
    8.3 weight:权重nginx
    8.4 proxy_pass:访问 nginx 的地址名称

声明:此博文为个人笔记!

【项目复习篇】EGO电商项目技术总结与学习笔记相关推荐

  1. django项目(天天生鲜电商项目)

    django项目(天天生鲜电商项目)代码如下: github链接(这个是自己写的源码,喜欢的给个星) 天天生鲜项目视频(这个是python全栈的视频,好不容易找到的, 这个包含天天生鲜项目自带的源码) ...

  2. 商城项目怎么包装?电商项目的策划技巧解析

    电商已经开始在人们的生活中逐渐延伸,并开始一步一步的影响着人们的生活.因此,在一个发展迅猛却又竞争激烈的行业中,项目包装自然就成为了商城项目推广运营的核心部分.实践证明,一个好的包装项目,是项目双方双 ...

  3. 大数据项目(三)————电商项目介绍(一)

    1.项目介绍 本项目主要用于互联网电商企业中,使用Spark技术开发的大数据统计分析平台,对电商网站的各种用户行为(访问行为.购物行为.广告点击行为等)进行复杂的分析.用统计分析出来的数据,辅助公司中 ...

  4. 【开源项目】八、电商项目dts-mall 聚惠星商城

    介绍 聚惠星商城 DTS-SHOP,基于 微信小程序 + springboot + vue 技术构建 ,支持单店铺,多店铺入驻的商城平台.项目包含 微信小程序,管理后台.基于java后台语言,已功能闭 ...

  5. java 电商项目 搜索模块,SSH电商项目实战之十:商品类基本模块的搭建

    前面我们完成了与商品类别相关的业务逻辑,接下来我们开始做具体商品部分. 1. 数据库建表并映射Model 首先我们在数据库中新建一张表,然后使用逆向工程将表映射成Model类,表如下: SQL代码/* ...

  6. Spring Boot电商项目

    Spring Boot电商项目 一.概述 1.电商项目整体介绍 (1)前台模块具体分析 (2)后台模块具体分析 (3) 项目演示 2 . 项目开发所需工具准备 二. 数据库设计于项目初始化 1.表设计 ...

  7. Django项目之Web端电商网站的实战开发(一)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 项目源码下载 目录 一丶项目介绍 二丶电商项目开发流程 三丶项目需求 四丶项目架构概览 五丶项目数据库设计 六丶项目框架搭建 一丶 ...

  8. 大数据 互联网架构阶段 电商项目简介

    电商项目简介 零.目录 电商项目特点 电商项目技术难点 电商项目简介 开发工具 电商项目架构 开发环境 一.电商项目特点 分布式 数十台服务器,甚至百台.千台.万台,包括:Nigix负载均衡集群.To ...

  9. JavaEE大型分布式电商项目 上海淘淘商城 29期

    上海29期_张志君老师_淘淘商城_大型分布式电商项目 JavaEE大型分布式电商项目 淘淘商城 29期 需要的加qq:350226234,备注:程序员学习视频 ==================== ...

  10. 微服务项目之电商--9.商城架构图及商城管理系统前端页面介绍及电商项目初步搭建(1)

    目录 一.商城架构图 前端: 二.商城管理系统前端页面 1.SPA介绍 2.webpack 四个核心概念 3.vue-cli 安装 4.项目测试 三.电商项目搭建 创建父模块管理 创建子模板注册中心l ...

最新文章

  1. 前端自动化构建工具webpack (二)之css和插件加载总结
  2. 风雨飘摇中的HP会分拆Arcsight业务吗?
  3. php类的实例化和调用,PHP:如何使用另一个类中的参数实例化一个类
  4. 团队作业8——测试与发布(Beta阶段)
  5. 深度学习资料汇总(满满的干货)
  6. 【leetcode】Search for a Range
  7. linuxsed替换字符串后保存_Numpy运用-文件读写、存储及字符串处理
  8. Opencv--Mat属性step,size,step1,elemSize,elemSize1
  9. centos 6.6 mysql5.7_centos6.6 下安装mysql5.7
  10. spatial Statistics
  11. CSDN VIP文章
  12. Type-C扩展坞自适应供电专利维权案例
  13. 肿瘤信息学中一些专业词汇整理(不断更新ing)
  14. 【屏幕灯】MI电脑显示器灯条用户手册
  15. CPU方案简介SSD201 - 智能网关中控
  16. 烟草行业IT规划现状、实施及工作重点分析
  17. Unity 3D 可展开公告牌
  18. Tornado+Pyecharts+LayUI搭建个人足迹地图服务
  19. ROS入门跟着我就够了(二)上 ROS通信机制
  20. Android的隐式跳转

热门文章

  1. 软件测试价值提升之路--第2部分“扫门前雪”-第3章“拦截缺陷”-读书笔记
  2. 计算机论文如何加字数,发表论文的字数是如何计算的?
  3. 《计算机科学概论》目录—导读
  4. 怎么利用计算机求一元三次方程,一元三次方程怎么快速把解求出来?
  5. 标准二维表问题 (卡特兰数)
  6. CharField:Django文档——Model字段选项(Field Options)
  7. 浅谈JSONP跨域漏洞
  8. java.lang.NoClassDefFoundError: org/apache/hive/service/cli/thrift/TCLIService$Iface
  9. 计算机外文文献PDF,computer network 计算机 网络 外文文献.pdf
  10. 表格操作系列——字段名与字段别名的获取