设计模式-服务定位器模式
背景
记得刚入行的时候,有一个需求通过用户每次来获取一个固定商品的信息,而我的实现是直接从库中查出再转成对象,然后返回给用户,突然某一天用户量增加了好几百倍,直接把库拉挂了...后面直接用上了缓存,解决了该问题...
服务定位器模式是什么?
服务定位器模式(Service Locator Pattern)用在我们想使用 JNDI 查询定位各种服务的时候。考虑到为某个服务查找 JNDI 的代价很高,服务定位器模式充分利用了缓存技术。在首次请求某个服务时,服务定位器在 JNDI 中查找服务,并缓存该服务对象。当再次请求相同的服务时,服务定位器会在它的缓存中查找,这样可以在很大程度上提高应用程序的性能。
角色:
服务(Service):实际处理请求的服务。对这种服务的引用可以在 JNDI 服务器中查找到。
Context / 初始的 Context :JNDI Context 带有对要查找的服务的引用。
服务定位器(Service Locator):服务定位器是通过 JNDI 查找和缓存服务来获取服务的单点接触。
缓存(Cache):缓存存储服务的引用,以便复用它们。
客户端(Client):Client 是通过 ServiceLocator 调用服务的对象。
优点:
提升系统性能:由于该模式可以将每次需要返回的固定数据放到缓存中可以减少每次创建对象的开销,很大程度上提升了系统的性能;
缺点:
缓存的实现使系统会比较复杂,如果控制不好会导致内存溢出。
服务定位器模式可以干嘛?
服务定位器模式主要解决一个相同或者公共的数据放到缓存中减少内存开销,并且在一定程序上提升了系统的性能。
个人理解:
比如数据库连接池,不需要每次去连接数据库的时候去创建连接,而是由池去保持一些连接,当需要连接数据库的时候直接获取池中的连接就可以了。
服务定位器模式类图
源码下载:https://gitee.com/hong99/design-model/issues/I1IMES
实现代码
/**
* @Auther: csh
* @Date: 2020/7/5 11:44
* @Description:抽象的服务接口
*/
public interface IUserInfo {
public String getName();
public void printUserInfo();
}
/**
* @Auther: csh
* @Date: 2020/7/5 11:45
* @Description:用户信息(Service)
*/
public class User implements IUserInfo {
private String userName;
public void setUserName(String userName) {
this.userName = userName;
}
private User() {
}
public User(String usreName) {
this.userName = usreName;
}
@Override
public String getName() {
return userName;
}
@Override
public void printUserInfo() {
System.out.println("用户信息:"+this.getName());
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + ''' +
'}';
}
}
/**
* @Auther: csh
* @Date: 2020/7/5 11:48
* @Description:创建对象(Context )
*/
public class InitialContext {
private static InitialContext initialContext = new InitialContext();
private InitialContext(){
}
public static InitialContext getInstance(){
return initialContext;
}
public Object lookup(String username){
return new User(username);
}
}
/**
* @Auther: csh
* @Date: 2020/7/5 11:51
* @Description:用户缓存
*/
public class UserCache {
private List<IUserInfo> userInfos;
public UserCache() {
userInfos = new ArrayList <IUserInfo>();
}
public IUserInfo getUserInfo(String userName){
for (IUserInfo userInfo : userInfos) {
if(userInfo.getName().equalsIgnoreCase(userName)){
System.out.println("从缓存中获取到用户信息:"+userInfo.toString());
return userInfo;
}
}
return null;
}
public void addUserInfo(IUserInfo userInfo){
boolean exist = false;
for (IUserInfo info : userInfos) {
if(info.getName().equalsIgnoreCase(userInfo.getName())){
exist = true;
}
}
if(!exist){
userInfos.add(userInfo);
}
}
}
/**
* @Auther: csh
* @Date: 2020/7/5 12:06
* @Description:服务定位器(Service Locator)
*/
public class UserInfoLocator {
private static UserCache userCache = new UserCache();
public static IUserInfo getUserInfo(String userName){
IUserInfo userInfo = userCache.getUserInfo(userName);
if(userInfo!=null){
return userInfo;
}
InitialContext instance = InitialContext.getInstance();
IUserInfo user = (IUserInfo)instance.lookup(userName);
userCache.addUserInfo(user);
return user;
}
}
/**
* @Auther: csh
* @Date: 2020/7/5 12:51
* @Description:服务定位器模式
*/
public class Client {
public static void main(String[] args) {
IUserInfo userInfo = UserInfoLocator.getUserInfo("user1");
userInfo.printUserInfo();
userInfo = UserInfoLocator.getUserInfo("user2");
userInfo.printUserInfo();
userInfo = UserInfoLocator.getUserInfo("user1");
userInfo.printUserInfo();
}
}
结果
用户信息:user1
用户信息:user2
从缓存中获取到用户信息:User{userName='user1'}
用户信息:user1
源码下载:https://gitee.com/hong99/design-model/issues/I1IMES
最后
服务定位器模式就是将公共或者频繁使用的信息放到缓存中,然后当用户再去获取的时候可以直接从缓存中获取,减少系统开辟内存空间的开销并且可以提升系统性能,大幅度提升资源的利用率。该模式开发中无处不用数据库连接池中的数据库连接、配置信息(账号、密码、连接地址)放到缓存中、spring的一些bean注入等,但是该模式要注意放入缓存中的信息需要添加过期时候,否则可能缓存越堆越大会导致内存溢出问题...
参考文章:
https://www.oracle.com/technetwork/java/servicelocator-137181.html
http://gameprogrammingpatterns.com/service-locator.html
- 算法系列(二)
- JavaScript 基础(五) 函数 变量和作用域
- iOS8 、iPhone6 及iPhone6+:Apple touch icon 与Startup Image
- 算法系列
- .net页面生命周期
- JavaScript 基础(四) 循环
- 【译】WordPress 中的50个过滤器(6):第41-50个过滤器
- 【译】WordPress 中的50个过滤器(5):第31-40个过滤器
- 使用VS2010 C#开发ActiveX控件
- JavaScript 基础(三) 对象 条件判断
- “大数据+定制化服务”或将引领高端旅游市场
- FrameLayout(框架布局)
- WordPress 中变量$allowedposttags 添加自定义HTML属性
- 移动端UC /QQ 浏览器的部分私有Meta 属性
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 基于consul的Docker-overlay跨多宿主机容器网络
- Python 微信机器人-向指定名称的好友发送微信消息
- JavaScript技术篇-js提升网页视频播放速率,提高慕课网视频播放速度
- 使用docker五步搭建ELK日志收集分析系统
- HDFS之SequenceFile和MapFile
- 配置ELK技术栈来分析apache tomcat日志
- LVS DR模式搭建,keepalived + lvs
- Ubuntu安装docker
- DockerFile简介与实例
- Elasticsearch2.3官方Dockerfile解析
- openstack架构解析
- Linux服务器基础网络配置
- 负载均衡集群介绍,LVS介绍,LVS的调度算法,LVS的NAT模式搭建
- 03 实战 Ansible-Playbook之初始化服务器
- Dubbo与Zookeeper,SpringMVC整合和使用