首页 文章 精选 留言 我的

精选列表

搜索[响应],共10006篇文章
优秀的个人博客,低调大师

通过 MapReduce 降低服务响应时间

在微服务中开发中,api网关扮演对外提供restful api的角色,而api的数据往往会依赖其他服务,复杂的api更是会依赖多个甚至数十个服务。虽然单个被依赖服务的耗时一般都比较低,但如果多个服务串行依赖的话那么整个api的耗时将会大大增加。 那么通过什么手段来优化呢?我们首先想到的是通过并发来的方式来处理依赖,这样就能降低整个依赖的耗时,Go基础库中为我们提供了WaitGroup工具用来进行并发控制,但实际业务场景中多个依赖如果有一个出错我们期望能立即返回而不是等所有依赖都执行完再返回结果,而且WaitGroup中对变量的赋值往往需要加锁,每个依赖函数都需要添加Add和Done对于新手来说比较容易出错 基于以上的背景,go-zero框架中为我们提供了并发处理工具MapReduce,该工具开箱即用,不需要做什么初始化,我们通过下图看下使用MapReduce和没使用的耗时对比: 相同的依赖,串行处理的话需要200ms,使用MapReduce后的耗时等于所有依赖中最大的耗时为100ms,可见MapReduce可以大大降低服务耗时,而且随着依赖的增加效果就会越明显,减少处理耗时的同时并不会增加服务器压力 并发处理工具MapReduce MapReduce是Google提出的一个软件架构,用于大规模数据集的并行运算,go-zero中的MapReduce工具正是借鉴了这种架构思想 go-zero框架中的MapReduce工具主要用来对批量数据进行并发的处理,以此来提升服务的性能 我们通过几个示例来演示MapReduce的用法 MapReduce主要有三个参数,第一个参数为generate用以生产数据,第二个参数为mapper用以对数据进行处理,第三个参数为reducer用以对mapper后的数据做聚合返回,还可以通过opts选项设置并发处理的线程数量 场景一: 某些功能的结果往往需要依赖多个服务,比如商品详情的结果往往会依赖用户服务、库存服务、订单服务等等,一般被依赖的服务都是以rpc的形式对外提供,为了降低依赖的耗时我们往往需要对依赖做并行处理 funcproductDetail(uid,pidint64)(*ProductDetail,error){ varpdProductDetail err:=mr.Finish(func()(errerror){ pd.User,err=userRpc.User(uid) return },func()(errerror){ pd.Store,err=storeRpc.Store(pid) return },func()(errerror){ pd.Order,err=orderRpc.Order(pid) return }) iferr!=nil{ log.Printf("productdetailerror:%v",err) returnnil,err } return&pd,nil } 该示例中返回商品详情依赖了多个服务获取数据,因此做并发的依赖处理,对接口的性能有很大的提升 场景二: 很多时候我们需要对一批数据进行处理,比如对一批用户id,效验每个用户的合法性并且效验过程中有一个出错就认为效验失败,返回的结果为效验合法的用户id funccheckLegal(uids[]int64)([]int64,error){ r,err:=mr.MapReduce(func(sourcechan<-interface{}){ for_,uid:=rangeuids{ source<-uid } },func(iteminterface{},writermr.Writer,cancelfunc(error)){ uid:=item.(int64) ok,err:=check(uid) iferr!=nil{ cancel(err) } ifok{ writer.Write(uid) } },func(pipe<-chaninterface{},writermr.Writer,cancelfunc(error)){ varuids[]int64 forp:=rangepipe{ uids=append(uids,p.(int64)) } writer.Write(uids) }) iferr!=nil{ log.Printf("checkerror:%v",err) returnnil,err } returnr.([]int64),nil } funccheck(uidint64)(bool,error){ //dosomethingcheckuserlegal returntrue,nil } 该示例中,如果check过程出现错误则通过cancel方法结束效验过程,并返回error整个效验过程结束,如果某个uid效验结果为false则最终结果不返回该uid MapReduce使用注意事项 mapper和reducer中都可以调用cancel,参数为error,调用后立即返回,返回结果为nil, error mapper中如果不调用writer.Write则item最终不会被reducer聚合 reducer中如果不调用writer.Wirte则返回结果为nil, ErrReduceNoOutput reducer为单线程,所有mapper出来的结果在这里串行聚合 实现原理分析: MapReduce中首先通过buildSource方法通过执行generate(参数为无缓冲channel)产生数据,并返回无缓冲的channel,mapper会从该channel中读取数据 funcbuildSource(generateGenerateFunc)chaninterface{}{ source:=make(chaninterface{}) gofunc(){ deferclose(source) generate(source) }() returnsource } 在MapReduceWithSource方法中定义了cancel方法,mapper和reducer中都可以调用该方法,调用后主线程收到close信号会立马返回 cancel:=once(func(errerror){ iferr!=nil{ retErr.Set(err) }else{ //默认的error retErr.Set(ErrCancelWithNil) } drain(source) //调用close(ouput)主线程收到Done信号,立马返回 finish() }) 在mapperDispatcher方法中调用了executeMappers,executeMappers消费buildSource产生的数据,每一个item都会起一个goroutine单独处理,默认最大并发数为16,可以通过WithWorkers进行设置 varwgsync.WaitGroup deferfunc(){ wg.Wait()//保证所有的item都处理完成 close(collector) }() pool:=make(chanlang.PlaceholderType,workers) writer:=newGuardedWriter(collector,done)//将mapper处理完的数据写入collector for{ select{ case<-done://当调用了cancel会触发立即返回 return casepool<-lang.Placeholder://控制最大并发数 item,ok:=<-input if!ok{ <-pool return } wg.Add(1) gofunc(){ deferfunc(){ wg.Done() <-pool }() mapper(item,writer)//对item进行处理,处理完调用writer.Write把结果写入collector对应的channel中 }() } } reducer单goroutine对数mapper写入collector的数据进行处理,如果reducer中没有手动调用writer.Write则最终会执行finish方法对output进行close避免死锁 gofunc(){ deferfunc(){ ifr:=recover();r!=nil{ cancel(fmt.Errorf("%v",r)) }else{ finish() } }() reducer(collector,writer,cancel) }() 在该工具包中还提供了许多针对不同业务场景的方法,实现原理与MapReduce大同小异,感兴趣的同学可以查看源码学习 MapReduceVoid 功能和MapReduce类似但没有结果返回只返回error Finish 处理固定数量的依赖,返回error,有一个error立即返回 FinishVoid 和Finish方法功能类似,没有返回值 Map 只做generate和mapper处理,返回channel MapVoid 和Map功能类似,无返回 本文主要介绍了go-zero框架中的MapReduce工具,在实际的项目中非常实用。用好工具对于提升服务性能和开发效率都有很大的帮助,希望本篇文章能给大家带来一些收获。 项目地址 github.com/tal-tech/go-zero 欢迎使用 go-zero 并 star 支持我们! 微信交流群 关注『微服务实践』公众号并回复 进群 获取社区群二维码。

优秀的个人博客,低调大师

ViewPager实现Recycle机制和响应notifyDataSetChanged

1.目标 主界面要求水平移动翻页效果,每次只能翻一页,可以翻无数页。 2.实现思路 针对“每次只能翻一页”这个要求,简单使用SDK的话只有用ViewPager。ViewPager的PageAdapter是没有实现RecyclerView的ViewHolder.itemView回收机制的。即使是子类FragmentStatePagerAdapter,也只是保存状态后销毁Fragment,Fragment本身是在不断地创建和销毁,没有重复利用。 正确地使用PageAdapter,必须先理解Object instantiateItem (ViewGroup container, int position)函数中返回的Object的意义。如果View能重复利用,则表明它仅用于展示数据,没有业务逻辑。数据的部分,我们通常用Model表示。instantiateItem函数的返回值Object,正是要求返回在position位置的Model数据对象,与实现这个函数时创建的View形成映射关系。 3.自定义PageAdapter 直接在代码注释中讲解: // 转载请注明出处:http://blog.csdn.net/hursing private class ViewPageAdapter extends PagerAdapter { // 用List保存本来要销毁的View,需要的时候再取出来 private List<ItemView> mViewRecycler = new LinkedList<>(); // 用Map保存Model Object(这里是OnePiece)和View的映射关系 private Map<OnePiece, ItemView> mOnePieceItemViewMap = new HashMap<>(); @Override public int getCount() { // Model保存着OnePiece的对象数组 return mPieceModel.pieces.size(); } @Override public boolean isViewFromObject(View view, Object object) { // 如果不需要回收利用View,都是用`return view == object`实现的, // 与此对应`instantiateItem()`返回View, // `destroyItem()`要销毁View。 OnePiece onePiece = (OnePiece) object; return view == mOnePieceItemViewMap.get(onePiece); } @Override public Object instantiateItem (ViewGroup container, int position) { ItemView itemView; // 这个if else是实现回收利用的关键处之一。如果有缓存则取缓存, // 没有则创建对象 if (mViewRecycler.size() > 0) { itemView = mViewRecycler.remove(0); } else { itemView = (ItemView) LayoutInflater.from(MainActivity.this) .inflate(R.layout.item_view, container, false); } // container实际是ViewPager对象。从这个函数的实现看, // ViewPager并不知道对它add的View是做什么用的,所以 // 需要`isViewFromObject()`函数来询问这个View是否有 // 意义(是ItemView),如果是,后面会对它做正确layout container.addView(itemView); OnePiece onePiece = mPieceModel.pieces.get(position); // 这里是itemView根据Model object来重新设置界面元素的时机。 itemView.setOnePiece(onePiece); mOnePieceItemViewMap.put(onePiece, itemView); // 如果不需要回收利用View,这里的实现是`return itemView`。 // 要回收,就要建立Model object和View的映射关系,因此 // 这里返回Model object return onePiece; } @Override public void destroyItem (ViewGroup container, int position, Object object) { OnePiece onePiece = (OnePiece) object; ItemView itemView = mOnePieceItemViewMap.remove(onePiece); // 必须自行remove itemView container.removeView(itemView); // itemView要被回收放入缓存了,这里提供一个时机做清理。 itemView.reset(); // 实现回收的关键处,把itemView保存到List里。而不是任由GC销毁。 mViewRecycler.add(itemView); } // 必须重写此函数。super固定地`return POSITION_UNCHANGED`, // 这就会导致`notifyDataSetChanged()`没有效果,因为它的意义 // 是说这个Model object的位置没变化,自然不需要刷新。 @Override public int getItemPosition(Object object) { OnePiece onePiece = (OnePiece) object; if (mPieceModel.pieces.contains(onePiece)) { return mPieceModel.pieces.indexOf(onePiece); } else { // 返回POSITION_NONE说明这个model object已经被废弃了, // 接下来就会由`destroyItem()`回调来销毁它对应的View。 return POSITION_NONE; } } } 4.完整代码 代码放在 https://github.com/hursing/ViewPagerDemo 界面结构:5个按钮,分别是增、删、重置Model object。下面是ViewPager,按完按钮滚动区域会有变化。每一页有三个TextView,其中最上面的是显示ItemView本身的引用信息,留意它的引用地址(如截图中的12f46ad2),会发现最多创建4个ItemView(缓存了左、右、当前,还有一个预备),再怎么滑都会重复利用。 截图: 下面是贴些代码,可不看了: OnePiece.java public class OnePiece { public char letter; public int number; public OnePiece(char letter, int number) { this.letter = letter; this.number = number; } } item_view.xml <com.example.liuhx.viewpagerdemo.ItemView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ccc" android:orientation="vertical" android:paddingTop="40dp" android:paddingBottom="40dp" android:paddingLeft="20dp" android:paddingRight="20dp"> <TextView android:id="@+id/this_info" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="reference info" /> <TextView android:id="@+id/text1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="sample1" /> <TextView android:id="@+id/text2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="sample2" /> </com.example.liuhx.viewpagerdemo.ItemView> ItemView.java public class ItemView extends LinearLayout { private TextView mTextView1; private TextView mTextView2; private OnePiece mOnePiece; public ItemView(Context context, AttributeSet attrs) { super(context, attrs); } public void setOnePiece(OnePiece onePiece) { mOnePiece = onePiece; mTextView1.setText(String.valueOf(onePiece.letter)); mTextView2.setText(String.valueOf(onePiece.number)); } public void reset() { // Release resource here: network connection, database cursor, ... mOnePiece = null; } @Override public void onFinishInflate() { // To differentiate View objects. ((TextView) findViewById(R.id.this_info)).setText(this.toString()); mTextView1 = (TextView) findViewById(R.id.text1); mTextView2 = (TextView) findViewById(R.id.text2); } } PieceModel.java public class PieceModel { public interface OnPiecesChangedListener { void onPiecesChanged(); } public List<OnePiece> pieces = new LinkedList<>(); private OnPiecesChangedListener mOnPiecesChangedListener; private char mLetterSeed = 'A'; private int mNumberSeed = 0; public void setOnPiecesChangedListener(OnPiecesChangedListener listener) { mOnPiecesChangedListener = listener; } public void addPiece(boolean toTail) { OnePiece onePiece = new OnePiece(mLetterSeed, mNumberSeed++); if (toTail) pieces.add(onePiece); else pieces.add(0, onePiece); mOnPiecesChangedListener.onPiecesChanged(); } public void removePiece(boolean fromTail) { if (fromTail) pieces.remove(pieces.size() - 1); else pieces.remove(0); mOnPiecesChangedListener.onPiecesChanged(); } public void changePieces() { ++mLetterSeed; mNumberSeed = 0; int size = pieces.size(); pieces.clear(); for (; mNumberSeed < size; ++mNumberSeed) { OnePiece onePiece = new OnePiece(mLetterSeed, mNumberSeed); pieces.add(onePiece); } mOnPiecesChangedListener.onPiecesChanged(); } } activity_main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.liuhx.viewpagerdemo.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/add_head" android:text="add head"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/add_tail" android:text="add tail"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/remove_head" android:text="remove head"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/remove_tail" android:text="remove tail"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:id="@+id/change" android:text="change"/> </LinearLayout> <android.support.v4.view.ViewPager android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/view_pager" /> </LinearLayout> MainActivity.java public class MainActivity extends AppCompatActivity implements View.OnClickListener { private ViewPager mViewPager; private ViewPageAdapter mViewPageAdapter; private PieceModel mPieceModel; private class ViewPageAdapter extends PagerAdapter { private List<ItemView> mViewRecycler = new LinkedList<>(); private Map<OnePiece, ItemView> mOnePieceItemViewMap = new HashMap<>(); @Override public int getCount() { return mPieceModel.pieces.size(); } @Override public boolean isViewFromObject(View view, Object object) { OnePiece onePiece = (OnePiece) object; return view == mOnePieceItemViewMap.get(onePiece); } @Override public Object instantiateItem (ViewGroup container, int position) { ItemView itemView; if (mViewRecycler.size() > 0) { itemView = mViewRecycler.remove(0); } else { itemView = (ItemView) LayoutInflater.from(MainActivity.this) .inflate(R.layout.item_view, container, false); } container.addView(itemView); OnePiece onePiece = mPieceModel.pieces.get(position); itemView.setOnePiece(onePiece); mOnePieceItemViewMap.put(onePiece, itemView); return onePiece; } @Override public void destroyItem (ViewGroup container, int position, Object object) { OnePiece onePiece = (OnePiece) object; ItemView itemView = mOnePieceItemViewMap.remove(onePiece); container.removeView(itemView); itemView.reset(); mViewRecycler.add(itemView); } // Must override this method, or else notifyDataSetChanged() has no effect. @Override public int getItemPosition(Object object) { OnePiece onePiece = (OnePiece) object; if (mPieceModel.pieces.contains(onePiece)) { return mPieceModel.pieces.indexOf(onePiece); } else { return POSITION_NONE; } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPieceModel = new PieceModel(); mPieceModel.setOnPiecesChangedListener(new PieceModel.OnPiecesChangedListener() { @Override public void onPiecesChanged() { mViewPageAdapter.notifyDataSetChanged(); } }); mViewPager = (ViewPager) findViewById(R.id.view_pager); mViewPageAdapter = new ViewPageAdapter(); mViewPager.setAdapter(mViewPageAdapter); mViewPager.setOffscreenPageLimit(1); findViewById(R.id.add_head).setOnClickListener(this); findViewById(R.id.add_tail).setOnClickListener(this); findViewById(R.id.remove_head).setOnClickListener(this); findViewById(R.id.remove_tail).setOnClickListener(this); findViewById(R.id.change).setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.add_head: mPieceModel.addPiece(false); break; case R.id.add_tail: mPieceModel.addPiece(true); break; case R.id.remove_head: if (mPieceModel.pieces.size() == 0) { Toast.makeText(this, "Please press ADD first", Toast.LENGTH_SHORT).show(); } else { mPieceModel.removePiece(false); } break; case R.id.remove_tail: if (mPieceModel.pieces.size() == 0) { Toast.makeText(this, "Please press ADD first", Toast.LENGTH_SHORT).show(); } else { mPieceModel.removePiece(true); } break; case R.id.change: if (mPieceModel.pieces.size() == 0) { Toast.makeText(this, "Please press ADD first", Toast.LENGTH_SHORT).show(); } else { mPieceModel.changePieces(); } break; default: break; } } } 转载请注明出处:http://blog.csdn.net/hursing

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册